在C++14中,取3个通用参数,将它们转发到tuple中,将该tuple转发到一个新的构造函数(可能带有标记类型以帮助分派),并使用基于类型的
std::get
来提取每个类型。将其转发到另一个构造函数,带有标记以帮助调度。
使用SFINAE检查可以提供可选的早期失败。
struct Date {
private:
struct as_tuple{};
struct in_order{};
public:
template<class A,class B,class C,
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>
的函数。
检查y/m/d是否都存在的SFINAE可能比较困难,但也许不是必须的。
如果您的y/m/d类型足够简单,则添加移动/完美转发的优化是另一种不必要的改进。
构造函数和标记的转发技术基于分步完成任务而非一次性完成任务的想法。代码已经够奇怪了。
实现自己的
std::get<T>
很容易。使其成为SFINAE友好则有点困难:
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{};
template<class T, class...Ts>
using type_index = typename type_index_t<T,Ts...>::type;
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>>
{};
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>
可能是更好的选择。