Я не уверен, чего вы хотите достичь, однако похоже, что вы хотите предотвратить смену X и Y или даже автоматически обновить нужного участника, если это произойдет.
Джонатан Боккара имеет хороший пост в блоге об этом: https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/
Идея состоит в том, что вы пишете:
using X = NamedType<int, struct XTag>;
using Y = NamedType<int, struct YTag>;
struct Coord
{
X x;
Y y;
};
auto a = Coord{ X(4),Y(5) };
Если все по умолчанию может быть создано и типы вваша структура уникальна, вы можете сделать что-то с помощью шаблонов переменных, чтобы присвоить правильное значение нужному члену.
Конструктор будет выглядеть примерно так:
struct Coord
{
template<typename ...T>
Coord(T &&...t)
{
assignByType(std::forward_as_tuple(x, y), std::forward<T>(t)...);
}
X x{0};
Y y{0};
};
В зависимости от типов,Вы можете присвоить значения и даже утверждать, что все типы присутствуют и что ни один из них не игнорируется.
Реализация assignByType может быть:
#include <utility>
#include <tuple>
template<typename ... T, typename ... U, typename V>
auto assignByTypeHelper(std::tuple<T &...> t, V &&v, U && ... u)
{
std::get<V &>(t) = std::forward<V>(v);
if constexpr( sizeof...(U))
{
assignByTypeHelper(std::move(t), std::forward<U>(u)...);
}
}
template<typename ... T, typename ... U>
auto assignByType(std::tuple<T &...> t, U && ... u)
{
static_assert(sizeof...(T) == sizeof...(U));
return assignByTypeHelper(std::move(t), std::forward<U>(u)...);
}
Полный код в Проводник компилятора