SFINAE в конструкторах шаблонов классов

Я пытаюсь сделать что-то с шаблонами и SFINAE, в которой я начинающий. Я трачу огромное количество времени, чтобы заставить работать все самое простое. Можете ли вы помочь мне понять, как это работает?

Конструктор C< T, Ts …> принимает параметр T, который является либо A< U> или B< U>, но ведет себя по-разному в этих двух случаях. Я не могу показать вам все, что я пытался сделать. Вот способ, который показался мне наименее глупым.

template<typename T> class A{
public: A(){} };

template<typename T> class B{
public: B(){} };

template<typename T> struct enable_if_A         {};
template<typename T> struct enable_if_A< A<T> > {typedef A<T> type;};

template<typename T> struct enable_if_B         {};
template<typename T> struct enable_if_B< B<T> > {typedef B<T> type;};

template<typename T,typename... Ts> class C{
public:
C(typename enable_if_A<T>::type const &p){cout << "A" << endl;}
C(typename enable_if_B<T>::type const &p){cout << "B" << endl;}
};

// ...

A<float> a;
B<float> b;

C<A<float> > ca(a); // error: no type named ‘type’ in ‘struct enable_if_B<A<float> >'
C<B<float> > cb(b); // error: no type named ‘type’ in ‘struct enable_if_A<B<float> >'

Примечание: я использую g ++ (Ubuntu / Linaro 4.6.1-9ubuntu3) 4.6.1. Должен ли я обновить его?

Спасибо

Изменить: для более подробной информации, я также попытался (в частности):

template<typename T,typename... Ts> class C{
public:
template<>
C(typename enable_if_A<T>::type const &p){cout << "A" << endl;}
template<>
C(typename enable_if_B<T>::type const &p){cout << "B" << endl;}
};
//explicit specialization in non-namespace scope ‘class bcifs::C<T, Ts>’

//////////////////////////////////////////////////////////////////////////

template<typename T,typename... Ts> class C{
public:
template<typename E=void>
C(typename enable_if_A<T>::type const &p){cout << "A" << endl;}
template<typename E=void>
C(typename enable_if_B<T>::type const &p){cout << "B" << endl;}
};
// error: no type named ‘type’ in ‘struct enable_if_B<A<float> >'

//////////////////////////////////////////////////////////////////////////

template<typename T> struct enable_if_A         {};
template<typename T> struct enable_if_A< A<T> > {typedef void type;};

template<typename T> struct enable_if_B         {};
template<typename T> struct enable_if_B< B<T> > {typedef void type;};

template<typename T,typename... Ts> class C{
public:
template<typename E=void>
C(T const &p);

C<typename enable_if_A<T>::type>(T const &p){cout << "A" << endl;}
C<typename enable_if_B<T>::type>(T const &p){cout << "B" << endl;}
};
// error: invalid declarator before ‘(’ token

//////////////////////////////////////////////////////////////////////////

template<typename T> class C{
public:
template<>
C(T const &p,typename enable_if_A<T>::type * = 0){cout << "A" << endl;}
template<>
C(T const &p,typename enable_if_B<T>::type * = 0){cout << "B" << endl;}
};
// error: explicit specialization in non-namespace scope ‘class C<T>’
// error: no type named ‘type’ in ‘struct enable_if_B<A<float> >’

//////////////////////////////////////////////////////////////////////////

template<typename T> class C{
public:
template<typename U>
C(T const &p,typename enable_if_A<T>::type * = 0){cout << "A" << endl;}
template<typename U>
C(T const &p,typename enable_if_B<T>::type * = 0){cout << "B" << endl;}
};
// error: no type named ‘type’ in ‘struct enable_if_B<A<float> >’
// error: no matching function for call to ‘C<A<float> >::C(A<float>&)’

//////////////////////////////////////////////////////////////////////////

template<typename T> struct enable_if_A         {};
template<typename T> struct enable_if_A< A<T> > {typedef void type;};

template<typename T> struct enable_if_B         {};
template<typename T> struct enable_if_B< B<T> > {typedef void type;};

template<typename T> class C{
public:
template <typename U>
C(A<U> const & r, void* _ = 0);
};

template <typename T>
template <typename U>
C<T>::C<T>(A<U> const & r, typename enable_if_A<U>::type* _ = 0) {
cout << "A" << endl;
}
// error: ISO C++ forbids declaration of ‘C’ with no type [-fpermissive]
// error: function template partial specialization ‘C<T>’ is not allowed
// error: no ‘int C<T>::C(const A<U>&, typename enable_if_A<U>::type*)’ member function declared in class ‘C<T>’
// C<T>::C<U>(... does the same

Извините, но мне так и не удалось запустить ваши решения. Я наконец нашел:

// dummy-function-parameter-ed version :

template<typename T> class C{
public:
template <typename U>
C(A<U> const &r,typename enable_if<is_same<A<U>,T>::value>::type* = 0){cout << "A" << endl;}

template <typename U>
C(B<U> const &r,typename enable_if<is_same<B<U>,T>::value>::type* = 0){cout << "B" << endl;}
};

// and the dummy-template-parameter-ed version :

template<typename T> class C{
public:
template<typename U,typename E = typename enable_if<is_same<A<U>,T>::value>::type>
C(A<U> &r){cout << "A" << endl;}

template<typename U,typename E = typename enable_if<is_same<B<U>,T>::value>::type>
C(B<U> &r){cout << "B" << endl;}
};

3

Решение

template<typename T,typename... Ts> class C{
public:
C(typename enable_if_A<T>::type const &p){cout << "A" << endl;}
C(typename enable_if_B<T>::type const &p){cout << "B" << endl;}
};

Это неправильно, но вы уже это знали :). Причина в том, что SFINAE можно применять только на уровне шаблона, но вы пытаетесь применить его к элементу шаблона. То есть SFINAE в вашем шаблоне выше может применяться только к разным C<T> типы, но не конструкторы C<T>,

Чтобы иметь возможность применять SFINAE к конструктору, вам нужно сделать конструктор шаблоном. Но в вашем случае это приведет к другому ограничению. Конструкторы — это специальные функции, для которых нельзя предоставить аргумент шаблона (даже если конструктор является шаблонным), что означает, что тип шаблона должен быть выведен из места вызова. Но вложенные типы не выводятся …

Вы можете обойти ограничение, изменив сигнатуру конструктора:

template <typename T>
template <typename U>
C<T>::C<U>(A<U> const & r, typename enable_if_A<U>::type* _ = 0) {
// ...
}

В этом случае класс C шаблон, который имеет шаблонный конструктор, принимающий A<U>, который может использоваться только для типов, для которых enable_if_A<U>::type это действительно тип. Тип может быть выведен в месте вызова через первый аргумент, а выведенный тип U будет подставлен на второй аргумент. Если эта замена не удалась, шаблонный конструктор будет отброшен.

Вышеуказанное решение совместимо с C ++ 03. Если у вас есть компилятор C ++ 11, вы можете сделать то же самое без необходимости в дополнительном аргументе для конструктора (т.е. без добавления дополнительного аргумента; не на 100%, если я правильно понял синтаксис :)):

template <typename T>
template <typename U, typename _ = typename enable_if_A<U>::type>
C<T>::C<U>(U const &) {...}
8

Другие решения

Если вы не настаиваете на использовании SFINAE, вы можете легко решить эту проблему, используя косвенный конструктор с частичной специализацией, например так:

#include <iostream>

using namespace std;

template <typename T>
struct C
{
T val;
C() { init(); };
void init() {};
};

template<> void C<string>::init() { val = "STR"; }
template<> void C<int>::init() { val = 5; }

int main () {
C<int> x;
C<string> y;

cout << y.val << endl << x.val << endl;
}

Этот пример использует int и string, а не ваши типы A< U> и B< U>, но это не должно иметь значения (за исключением того, что это делает этот пример компилируемым).

0

По вопросам рекламы [email protected]