В C++ 14, принимают 3 общие аргументы, направить их в кортеж, направить этот кортеж нового конструктора (возможно с типом тега, чтобы помочь отправки), а также использовать тип на основе std::get
, чтобы вырезать каждый тип. Перейдите к еще одному конструктору с тегом, чтобы помочь в dispatchimg.
SFINAE проверяет, чтобы досрочный отказ был опциональным.
struct Date {
private:
struct as_tuple{};
struct in_order{};
public:
template<class A,class B,class C,
// SFINAE test based on type_index below:
class=decltype(
type_index<Year,A,B,C>{}+type_index<Month,A,B,C>{}+type_index<Day,A,B,C>{}
)
>
Date(A a,B b,C c):
Date(as_tuple{},
std::make_tuple(std::move(a),std::move(b),std::move(c))
)
{}
private:
template<class...Ts>
Date(as_tuple, std::tuple<Ts...> t):
Date(in_order{},
std::get<Year>(t),std::get<Month>(t),std::get<Day>(t)
)
{}
Date(in_order,Year y_,Month m_,Day d_):
y(y_),m(m_),d(d_)
{}
};
В C++ 11, вы можете реализовать свой собственный эквивалент std::get<T>
.
SFINAE проверяет, что y/m/d все присутствующие сложнее, но, возможно, не нужны.
Оптимизация (добавление переадресации/совершенная переадресация) - это еще одно улучшение, которое может не понадобиться, если ваши типы y/m/d достаточно просты.
Техника переадресации конструкторов и тегов основана на идее делать одну вещь за раз, а не сразу. Код будет довольно странным.
Реализация собственных std::get<T>
проста. Сделать его SFINAE дружественным немного сложнее:
// helpers to keep code clean:
template<std::size_t n>
using size=std::integral_constant<std::size_t, n>;
template<class T>struct tag{using type=T;};
template<class T, class...Ts>
struct type_index_t{}; // SFINAE failure
// client code uses this. Everything else can go in namespace details:
template<class T, class...Ts>
using type_index = typename type_index_t<T,Ts...>::type;
// found a match!
template<class T, class...Ts>
struct type_index_t<T, T, Ts...>:
tag<size<0>>
{};
template<class T, class T0, class...Ts>
struct type_index_t<T, T0, Ts...>:
tag<size<type_index<T,Ts...>::value+1>>
{};
// SFINAE (hopefully) std::get<T>:
template<class T, class...Ts>
auto my_get(std::tuple<Ts...>& tup)
-> decltype(std::get< type_index<T,Ts...>::value >(tup)) {
return std::get< type_index<T,Ts...>::value >(tup);
}
template<class T, class...Ts>
auto my_get(std::tuple<Ts...> const& tup)
-> decltype(std::get< type_index<T,Ts...>::value >(tup)) {
return std::get< type_index<T,Ts...>::value >(tup);
}
template<class T, class...Ts>
auto my_get(std::tuple<Ts...>&& tup)
-> decltype(std::get< type_index<T,Ts...>::value >(std::move(tup))) {
return std::get< type_index<T,Ts...>::value >(std::move(tup));
}
но это всего лишь непроверенный эскиз. Рассмотрение предложений по C++ 14 std::get<Type>
, вероятно, является лучшей идеей.
Это можно сделать, но я собираюсь утверждать, что этого не должно быть сделано. За исключением перегрузки оператора, операции которого должны быть коммутативными, вы действительно не должны обучать своих пользователей думать, что порядок аргументов не имеет значения. Это может закончиться только слезами. – KevinZ