在 C++14 中,采用 3 个泛型参数,将它们转发给一个元组,将该元组转发给一个新的构造函数(可能带有一个标记类型以帮助调度),并使用基于类型std::get
来提取每种类型。将其转发给另一个构造函数,并带有一个标签以帮助调度。
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>
可能是一个更好的主意。