C++ how to generate all the permutations of function overloads?

后端 未结 2 741
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-05 07:52

Lets say I have classes Date and classes Year, Month and Day.

struct Date {
  Date(Year year, Month month         


        
相关标签:
2条回答
  • 2020-12-05 08:36

    In C++14, take 3 generic arguments, forward them to a tuple, forward that tuple to a new constructor (possibly with a tag type to aid dispatch), and use the type-based std::get to exctract each type. Forward that to yet another constructor, with a tag to aid in dispatchimg.

    SFINAE checks to provide for early failure optional.

    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_)
      {}
    };
    

    In C++11, you can implement your own equivalent of std::get<T>.

    SFINAE checks that y/m/d are all present are harder, but maybe not needed.

    Optimization (adding move/perfect forwarding) is another improvement that may not be needed if your y/m/d types are simple enough.

    The technique of forwarding constructors and tags is based on the idea of doing one thing at a time, instead of all at once. The code is going to be already strange enough.

    Implementing your own std::get<T> is easy. Making it SFINAE friendly a bit harder:

     // 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));
    }
    

    but that is just an untested sketch. Looking at the proposals for C++14 std::get<Type> is probably a better idea.

    0 讨论(0)
  • 2020-12-05 08:43

    With C++14, you may do:

    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: if you would also allow default argument, you may do something like:

    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);
    }
    

    And then

    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 讨论(0)
提交回复
热议问题