Trong C++ 14, mất 3 lập luận chung chung, chuyển chúng vào một tuple, chuyển tiếp tuple rằng để một constructor mới (có thể với một loại thẻ để hỗ trợ công văn), và sử dụng các loại dựa trên std::get
để loại trừ từng loại. Chuyển tiếp đến một hàm tạo khác, với một thẻ để hỗ trợ trong dispatchimg.
SFINAE kiểm tra để cung cấp cho thất bại sớm tùy chọn.
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_)
{}
};
Trong C++ 11, bạn có thể thực hiện tương đương của riêng bạn std::get<T>
.
SFINAE kiểm tra rằng y/m/d là tất cả hiện diện khó hơn, nhưng có thể không cần thiết.
Tối ưu hóa (thêm di chuyển/chuyển tiếp hoàn hảo) là một cải tiến khác có thể không cần thiết nếu các loại y/m/d của bạn đủ đơn giản.
Kỹ thuật chuyển tiếp và thẻ xây dựng dựa trên ý tưởng làm một việc cùng một lúc, thay vì tất cả cùng một lúc. Mã này đã đủ lạ lùng rồi.
Triển khai std::get<T>
của riêng bạn thật dễ dàng. Làm cho SFINAE thân thiện hơn một chút:
// 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));
}
nhưng đó chỉ là một bản phác thảo chưa được kiểm tra. Nhìn vào các đề xuất cho C++ 14 std::get<Type>
có lẽ là một ý tưởng tốt hơn.
Điều này có thể được thực hiện, nhưng tôi sẽ cho rằng điều này không nên được thực hiện. Ngoại trừ việc quá tải một toán tử có hoạt động nên giao hoán, bạn thực sự không nên đào tạo người dùng của bạn nghĩ rằng thứ tự đối số không quan trọng. Điều này chỉ có thể kết thúc trong nước mắt. – KevinZ