СФИНА: одни неудачи более равны, чем другие? - PullRequest
5 голосов
/ 08 августа 2010

Я пытаюсь использовать SFINAE , чтобы отличить класс, в котором есть член с именем 'name'.Я настроил все в соответствии с тем, что кажется стандартным шаблоном, но он не работает - вместо молчаливого игнорирования подстановки «сбой» компилятор выдает ошибку.

Я уверен, что столкнулся скакое-то правило замены шаблонов, я был бы признателен, если бы кто-нибудь мог объяснить, какое из них.

Это упрощенный пример.Я использую gcc:

 template <typename U> string test( char(*)[sizeof(U::name)] = 0 ) { return "has name!"; }
 template <typename U> string test(...) { return "no name"; }

 struct HasName { string name; }
 struct NoName  {}

 cout << "HasName: " << test<HasName>(0) << endl;  //fine
 cout << "NoName: " << test<NoName>(0) << endl;    //compiler errors:

 //error: size of array has non-integral type `<type error>'
 //error: `name' is not a member of `NoName'

Ответы [ 2 ]

1 голос
/ 08 августа 2010

Следующее представляется действительным (хотя, как говорит Майкл, оно не обязательно дает желаемый результат на других компиляторах):

#include <string>
#include <iostream>
using namespace std;

template <typename U> string test( char(*)[sizeof(U::name)] = 0 ) { return "has name!"; }
template <typename U> string test(...) { return "no name"; }

struct HasName { static string name; };
struct NoName  { };

 int main() {
    cout << "HasName: " << test<HasName>(0) << endl;
    cout << "NoName: " << test<NoName>(0) << endl;
}

Вывод:

HasName: has name!
NoName: no name

gcc (GCC) 4.3.4 20090804 (выпуск) 1

Comeau также принимает код.

0 голосов
/ 09 августа 2010

Вот попытка сделать это:

// Tested on Microsoft (R) C/C++ Optimizing Compiler Version 15.00.30729.01
template<typename T>
class TypeHasName
{
private:
    typedef char (&YesType)[2];
    typedef char (&NoType)[1];

    struct Base { int name; };
    struct Derived : T, Base { Derived(); };

    template<typename U, U> struct Dummy;

    template<typename U>
    static YesType Test(...);

    template<typename U>
    static NoType Test(Dummy<int Base::*, &U::name>*);

public:
    enum { Value = (sizeof(Test<Derived>(0)) == sizeof(YesType)) };
};

#include <string>  
#include <iostream>  

struct HasName { std::string name; };  
struct NoName {};

int main()
{  
    std::cout << "HasName: " << TypeHasName<HasName>::Value << std::endl;  
    std::cout << "NoName: " << TypeHasName<NoName>::Value << std::endl;  
    return 0;
}

Идея состоит в том, что если T имеет переменную с именем name, то Derived будет иметь две name переменные (одна из * 1008)* и один из Base).Если T не объявляет переменную name, то Derived будет иметь только одну переменную из Base.

Если Derived имеет две name переменные, то выражение &U::name ввторая перегрузка Test() будет неоднозначной, и SFINAE должен удалить эту функцию из набора перегрузок.

...