2015-05-31 14 views
18

Cho phép nói rằng tôi có các lớp học Date và các lớp Year, MonthDay.C++ làm thế nào để tạo ra tất cả các hoán vị của quá tải chức năng?

struct Date { 
    Date(Year year, Month month, Day day) : d(day), m(month), y(year) {}; 
    Date(Month month, Day day, Year year) : d(day), m(month), y(year) {}; 
    Date(Day day, Month month, Year year) : d(day), m(month), y(year) {}; 
    Date(Day day, Year year, Month month) : d(day), m(month), y(year) {}; 
    ... 
    ... 

    private: 
    Day d; 
    Month m; 
    Year y; 
} 

Điều này cho phép tôi không có bố cục đối số cụ thể cho Date vì tôi bị quá tải.

Tôi có thể tự động tạo tất cả các hoán vị/quá tải không?

Chỉ cần được rõ ràng:

  • hoán vị là duy nhất của bố cục tranh luận, không có gì về họ nên thay đổi như tôi biết rằng sẽ không thể để tự động hóa.
  • Tất cả các quá tải được tạo nên có cùng mã chỉ với bố cục thay đổi đối số chứ không phải bản thân logic.
+2

Đ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

Trả lời

38

Với C++ 14, bạn có thể làm:

struct Date { 
public: 
    Date(const Year& year, const Month& month, const Day& day) : 
     d(day), m(month), y(year) 
    {} 

    template <typename T1, typename T2, typename T3> 
    Date(const T1& t1, const T2& t2, const T3& t3) : 
     Date(std::get<Year>(std::tie(t1, t2, t3)), 
      std::get<Month>(std::tie(t1, t2, t3)), 
      std::get<Day>(std::tie(t1, t2, t3))) 
    {} 

private: 
    Day d; 
    Month m; 
    Year y; 
}; 

Edit: nếu bạn cũng sẽ cho phép đối số mặc định, bạn có thể làm điều gì đó như:

namespace detail 
{ 
    template <typename T, typename... Ts> struct has_T; 

    template <typename T> struct has_T<T> : std::false_type {}; 

    template <typename T, typename... Ts> struct has_T<T, T, Ts...> 
    : std::true_type {}; 

    template <typename T, typename Tail, typename... Ts> 
    struct has_T<T, Tail, Ts...> : has_T<T, Ts...> {}; 

    template <typename T, typename... Ts> 
    const T& get_or_default_impl(std::true_type, 
           const std::tuple<Ts...>& t, 
           const T&) 
    { 
     return std::get<T>(t); 
    } 

    template <typename T, typename... Ts> 
    const T& get_or_default_impl(std::false_type, 
           const std::tuple<Ts...>&, 
           const T& default_value) 
    { 
     return default_value; 
    } 

    template <typename T1, typename T2> struct is_included; 

    template <typename... Ts> 
    struct is_included<std::tuple<>, std::tuple<Ts...>> : std::true_type {}; 

    template <typename T, typename... Ts, typename ... Ts2> 
    struct is_included<std::tuple<T, Ts...>, std::tuple<Ts2...>> : 
     std::conditional_t<has_T<T, Ts2...>::value, 
          is_included<std::tuple<Ts...>, std::tuple<Ts2...>>, 
          std::false_type> {}; 

} 

template <typename T, typename... Ts> 
const T& get_or_default(const std::tuple<Ts...>& t, 
         const T& default_value = T{}) 
{ 
    return detail::get_or_default_impl<T>(detail::has_T<T, Ts...>{}, t, default_value); 
} 

Và sau đó

struct Date { 
public: 
    Date(const Year& year, const Month& month, const Day& day) : 
     d(day), m(month), y(year) 
    {} 

    template <typename ... Ts, 
       typename std::enable_if_t< 
        detail::is_included<std::tuple<Ts...>, 
        std::tuple<Year, Month, Day>>::value>* = nullptr> 
    Date(const Ts&... ts) : 
     Date(get_or_default<const Year&>(std::tie(ts...)), 
      get_or_default<const Month&>(std::tie(ts...)), 
      get_or_default<const Day&>(std::tie(ts...))) 
    {} 

private: 
    Day d; 
    Month m; 
    Year y; 
}; 

Live Demo
Live Demo with invalid constructor call

+0

Cảm ơn bạn, đây là một giải pháp tao nhã mà tôi đang tìm kiếm :) –

+0

Bạn có thể thực hiện công việc này với các đối số mặc định không? – Gerard

+2

@Gerard: có thể thực hiện bằng cách thực hiện 'get_or_default'. – Jarod42

6

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.

Các vấn đề liên quan