#include <tuple>
#include <cstddef>
#include <utility>
#include <iostream>

enum class XParameters : unsigned int
{
	PARAMETER1, // int
	PARAMETER2, // float
	MAX,
};

enum class YParameters : unsigned int
{
	PARAMETER3 = XParameters::MAX // std::string
};

using XTuple = std::tuple<int, float>;
using YAttributes = std::tuple<std::string>;
using YTuple = decltype(tuple_cat(XTuple(), YAttributes()));

template <typename Attributes>
struct Common
{
	Common(Attributes&& attr) : attributes(std::move(attr)) {}
	
	Attributes attributes;

	template <typename AttributeName>
    auto GetAttribute(AttributeName attributeName) -> decltype(std::declval(std::get<static_cast<size_t>(attributeName)>(attributes)))
    {
    	return std::get<static_cast<size_t>(attributeName)>(attributes);
    }
};

struct X : Common<XTuple>
{
	X() : Common(std::make_tuple(42, 3.14f)) {}
};

struct Y : Common<YTuple>
{
	Y() : Common(std::make_tuple(666, 0.01f, "string")) {}
};

int main()
{
	X x;
	Y y;
	
	int parameter1 = std::get<static_cast<size_t>(XParameters::PARAMETER1)>(x.attributes); // Compiles, works.
	std::cout << parameter1 << std::endl;

	std::string parameter3 = std::get<static_cast<size_t>(YParameters::PARAMETER3)>(y.attributes); // Compiles, works.
	std::cout << parameter3 << std::endl;
	
	x.GetAttribute(XParameters::PARAMETER1); // Does not compile.
	
	//parameter3 = std::get<static_cast<size_t>(YParameters::PARAMETER3)>(x.attributes); // Does not compile, phew...

	
	
	
	return 0;
}