sobrecarga resueltos programacion poo orientada operadores objetos metodos funciones ejercicios ejemplos constructores clases clase c++ c++11 constructor-overloading

resueltos - ¿C++ cómo generar todas las permutaciones de sobrecargas de funciones?



sobrecarga de metodos c++ (2)

Digamos que tengo clases Date y clases Year , Month y Day .

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

Esto me permite no tener un diseño específico de argumentos para la Date ya que tengo muchas sobrecargas.

¿Puedo generar automáticamente todas las permutaciones / sobrecargas?

Para ser claro:

  • Las permutaciones son solo de diseño de argumentos, nada de ellos debería cambiar ya que sé que no sería posible automatizar.
  • Todas las sobrecargas generadas deben tener el mismo código, ya que solo el diseño de los argumentos no cambia la lógica en sí.

Con C ++ 14, puedes hacer:

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

Editar: si también permites un argumento predeterminado, puedes hacer algo como:

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

Y entonces

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

Demo en vivo
Demo en vivo con llamada de constructor inválido


En C ++ 14, tome 3 argumentos genéricos, reenvíelos a una tupla, reenvíe esa tupla a un nuevo constructor (posiblemente con un tipo de etiqueta para ayudar al envío), y use el std::get basado en tipo para extraer cada tipo. Envíe eso a otro constructor, con una etiqueta para ayudar en dispatchimg.

SFINAE realiza comprobaciones para prever fallos anticipados opcionales

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

En C ++ 11, puedes implementar tu propio equivalente de std::get<T> .

SFINAE comprueba que y / m / d están todos más presentes, pero es posible que no sean necesarios.

La optimización (agregar movimiento / reenvío perfecto) es otra mejora que puede no ser necesaria si sus tipos y / m / d son lo suficientemente simples.

La técnica de reenviar constructores y etiquetas se basa en la idea de hacer una cosa a la vez, en lugar de hacerlo de una vez. El código ya va a ser bastante extraño.

Implementar tu propio std::get<T> es fácil. Haciendo que SFINAE sea un poco más difícil:

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

pero eso es sólo un boceto no probado. Mirar las propuestas para C ++ 14 std::get<Type> es probablemente una mejor idea.