c++ c++11 constructor sfinae

c++ - ¿Cómo usar sfinae para seleccionar constructores?



c++11 (4)

La respuesta aceptada es buena para la mayoría de los casos, pero falla si hay dos sobrecargas de constructor con condiciones diferentes. Estoy buscando una solución en ese caso también.

Sí: la solución aceptada funciona pero no para dos constructores alternativos como, por ejemplo,

template <int otherN, typename = typename std::enable_if<otherN == 1>::type> explicit A(A<otherN> const &); template <int otherN, typename = typename std::enable_if<otherN != 1>::type> explicit A(A<otherN> const &);

Porque, como se indica en esta página ,

Un error común es declarar dos plantillas de función que difieren solo en sus argumentos de plantilla predeterminados. Esto es ilegal porque los argumentos de la plantilla predeterminada no forman parte de la firma de la plantilla de función, y declarar dos plantillas de función diferentes con la misma firma es ilegal.

Como se propone en la misma página, puede solucionar este problema aplicando SFINAE, modificando la firma, al tipo de parámetro de plantilla de valor (no tipo) de la siguiente manera

template <int otherN, typename std::enable_if<otherN == 1, bool>::type = true> explicit A(A<otherN> const &); template <int otherN, typename std::enable_if<otherN != 1, bool>::type = true> explicit A(A<otherN> const &);

En la meta programación de plantillas, se puede usar SFINAE en el tipo de retorno para elegir una determinada función de miembro de plantilla, es decir,

template<int N> struct A { int sum() const noexcept { return _sum<N-1>(); } private: int _data[N]; template<int I> typename std::enable_if< I,int>::type _sum() const noexcept { return _sum<I-1>() + _data[I]; } template<int I> typename std::enable_if<!I,int>::type _sum() const noexcept { return _data[I]; } };

Sin embargo, esto no funciona en los constructores. Supongamos, quiero declarar al constructor.

template<int N> struct A { /* ... */ template<int otherN> explicit(A<otherN> const&); // only sensible if otherN >= N };

pero no lo otherN < N para otherN < N .

Entonces, ¿ se puede usar SFINAE aquí? Solo me interesan las soluciones que permiten la deducción automática de parámetros de plantilla, de modo que

A<4> a4{}; A<5> a5{}; A<6> a6{a4}; // doesn''t compile A<3> a3{a5}; // compiles and automatically finds the correct constructor

Nota: este es un ejemplo muy simplificado donde SFINAE puede ser una exageración y static_assert puede ser suficiente. Sin embargo, quiero saber si puedo usar SFINAE en su lugar.


En C ++ 11, puede usar un parámetro de plantilla predeterminado:

template <int otherN, class = typename std::enable_if<otherN >= N>::type> explicit A(A<otherN> const &);

Sin embargo, si su compilador todavía no admite parámetros de plantilla predeterminados, o si necesita varias sobrecargas, entonces puede usar un parámetro de función predeterminado como este:

template <int otherN> explicit A(A<otherN> const &, typename std::enable_if<otherN >= N>::type* = 0);


Hay muchas formas de activar SFINAE, siendo enable_if solo una de ellas. Ante todo:

¿Qué es std :: enable_if?

Es solo esto:

template<bool, class T=void> enable_if{ typedef T type; }; template<class T> enable_if<false,T> {}; template<bool b, class T=void> using enable_if_t = typename enable_f<b,T>::type;

La idea es hacer que typename enable_if<false>::type sea ​​un error, por lo tanto, haga que se typename enable_if<false>::type cualquier declaración de plantilla que lo contenga.

Entonces, ¿cómo puede esta función de activación de selección?

Funciones de desactivacion

La idea es hacer la declaración errónea en alguna parte:

Por tipo de devolución

template<class Type> std::enable_if_t<cond<Type>::value,Return_type> function(Type);

Por un parámetro real

template<class Type> return_type function(Type param, std::enable_if_t<cond<Type>::value,int> =0)

Por un parámetro de plantilla

template<class Type, std::enable_if_t<cond<Type>::value,int> =0> //note the space between > and = return_type function(Type param)

Seleccionando funciones

Puedes parametrizar diferentes alternativas con trucos como este:

tempplate<int N> struct ord: ord<N-1>{}; struct ord<0> {}; template<class T, std::enable_if<condition3, int> =0> retval func(ord<3>, T param) { ... } template<class T, std::enable_if<condition2, int> =0> retval func(ord<2>, T param) { ... } template<class T, std::enable_if<condition1, int> =0> retval func(ord<1>, T param) { ... } template<class T> // default one retval func(ord<0>, T param) { ... } // THIS WILL BE THE FUCNTION YOU''LL CALL template<class T> retval func(T param) { return func(ord<9>{},param); } //any "more than 3 value"

Esto llamará a la primera / segunda / tercera / cuarta función si se cumple la condition2 , que condition2 que condition1 que ninguna de ellas.

Otros desencadenantes SFINAE

Escribir las condiciones de tiempo de compilación puede ser una cuestión de especialización explícita o una cuestión de éxito / fracaso de una evaluación no evaluada:

por ejemplo:

template<class T, class = void> struct is_vector: std::false_type {}; template<class X> struct is_vector<vector<X> >:: std::true_type {};

de modo que is_vector<int>::value es false pero is_vecttor<vector<int> >::value es true

O, por medio de la introspección , como

template<class T> struct is_container<class T, class = void>: std::false_type {}; template<class T> struct is_container<T, decltype( std::begin(std::declval<T>()), std::end(std::declval<T>()), std::size(std::declval<T>()), void(0))>: std::true_type {};

de modo que is_container<X>::value será true si se le da X x , puede compilar std::begin(x) etc.

El truco es que el decltype(...) es en realidad void (el operador , descarta las expresiones anteriores) solo si todas las sub-expresiones son compilables.

Incluso puede haber muchas otras alternativas. Espero que entre todo esto puedas encontrar algo útil.


Puede agregar un argumento de tipo predeterminado a la plantilla:

template <int otherN, typename = typename std::enable_if<otherN >= N>::type> explicit A(A<otherN> const &);