valor una tipos retorno prototipos programacion por paso parametros funciones funcion ejemplos con argumento c++ templates c++11 variadic-templates

c++ - una - Meta-iteración sobre argumentos de plantillas varadic



tipos de funciones en c (5)

Me gustaría generalizar el siguiente patrón:

template<class A1, class A2, class A3> class Foo { protected: template<class T> void foo(const T& t) {...do stuff...} public: void bar(const A1& a) { foo(a); } void bar(const A2& a) { foo(a); } void bar(const A3& a) { foo(a); } };

El enfoque anterior no se escala con una cantidad de argumentos crecientes. Entonces, me gustaría hacer:

template<class As...> class Foo { protected: template<class T> void foo(const t& a) {...do stuff...} public: for each type A in As declare: void bar(const A& a) { foo(a); } };

¿Hay una manera de hacerlo?


En caso de que realmente no necesite las bar y solo necesita restringir foo , podemos usar SFINAE para permitirle una llamada solo con un tipo convertible a una de las A s:

template <class... As> class Foo { public: template <class T, class = std::enable_if_t<any<std::is_convertible<T, As>::value...>::value>> void foo(T const&) { ... } };

Donde podemos implementar any con algo como el truco bool_pack :

template <bool... b> struct bool_pack { }; template <bool... b> using any = std::integral_constant<bool, !std::is_same<bool_pack<b..., false>, bool_pack<false, b...>>::value>;


Otro enfoque podría ser tener un control en la bar para probar si el tipo está en la secuencia, si no es así, con un mensaje de error útil, esto evitará cualquier trucos de herencia.

#include <iostream> struct E {}; struct F {}; template <class... As> class Foo { template <typename U> static constexpr bool contains() { return false; } template <typename U, typename B, typename ...S> static constexpr bool contains() { return (std::is_same<U, B>::value)? true : contains<U, S...>(); } protected: template <class T> void foo(const T& a) { std::cout << __PRETTY_FUNCTION__ << std::endl; } public: template <class T> void bar(const T& a) { static_assert(contains<T, As...>(), "Type does not exist"); foo(a); } }; int main() { Foo<E, F, E, F> f; f.bar(F{}); f.bar(E{}); f.bar(1); // will hit static_assert }


template <class A, class... As> class Foo : public Foo<As...> { protected: using Foo<As...>::foo; public: using Foo<As...>::bar; void bar(const A& a) { foo(a); } }; template <class A> class Foo<A> { protected: template <class T> void foo(const T& t) { } public: void bar(const A& a) { foo(a); } };

De manera similar a la respuesta de Piotr Skotnicki , esto usa la herencia para construir una clase con sobrecargas de bar para todos los argumentos de la plantilla. Sin embargo, es un poco más limpio, con solo una plantilla de clase más una especialización parcial.


template <class CRTP, class A, class... As> struct FooBar { void bar(const A& a) { static_cast<CRTP*>(this)->foo(a); } }; template <class CRTP, class A, class B, class... As> struct FooBar<CRTP, A, B, As...> : FooBar<CRTP, B, As...> { using FooBar<CRTP, B, As...>::bar; void bar(const A& a) { static_cast<CRTP*>(this)->foo(a); } }; template <class... As> class Foo : FooBar<Foo<As...>, As...> { template <class, class, class...> friend struct FooBar; protected: template <class T> void foo(const T& a) { } public: using FooBar<Foo, As...>::bar; };

MANIFESTACIÓN


template<class A, class Foo_t> class bar_t { public: void bar(const A &a) { Foo_t::foo(a); } }; template<class ...As> class Foo : bar_t<As, Foo<As...> >... { protected: template<class T> void foo(const T& a) { /* do stuff */ } };