2013-09-02 2 views
8

В C++ 11, чтобы узнать, есть ли класс функция члена size, вы можете определить следующий тест помощник:Использования SFINAE для обнаружения члена функции

template <typename T> 
struct has_size_fn 
{ 
    typedef char (& yes)[1]; 
    typedef char (& no)[2]; 

    template <typename C> static yes check(decltype(&C::size)); 
    template <typename> static no check(...); 

    static bool const value = sizeof(check<T>(0)) == sizeof(yes); 
}; 

Есть подобный трюк для выполнения это в C++ 98, не полагаясь на расширения компилятора, такие как typeof?

+0

Это также обнаружит элементы данных. – jrok

+1

Это может быть полезно: http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector - _Edit: _ также: http://stackoverflow.com/questions/257288/is-it-possible- to-write-ac-template-to-check-for-a-functions-существование (не останавливаться при первом ответе) –

+0

@jrok Это нормально, он будет использоваться в функции шаблона, которая затем вызовет компиляцию если это не функция - я могу жить с этим. – nijansen

ответ

8

На самом деле, ваше обнаружение потенциально ошибочны.

Проблема заключается в том, что все, что вы обнаружение, что C имеет элемент size:

  • это может быть признак
  • это может быть способом с любой подписью
  • там может быть даже несколько методы (с различными сигнатурами)

Если вы хотите затвердеть обнаружение, вы должны попытаться обнаружить только size (за все права). Here is such a hardened detection.

template <typename T> 
class has_size { 
private: 
    typedef char Yes; 
    typedef Yes No[2]; 

    template <typename U, U> struct really_has; 

    template <typename C> static Yes& Test(really_has <size_t (C::*)() const, 
             &C::size>*); 

    // EDIT: and you can detect one of several overloads... by overloading :) 
    template <typename C> static Yes& Test(really_has <size_t (C::*)(), 
             &C::size>*); 

    template <typename> static No& Test(...); 

public: 
    static bool const value = sizeof(Test<T>(0)) == sizeof(Yes); 
}; 

Edit:with overloads.

Уловка с неправильными size членами является структурой really_has. Я не претендую, что она идеально подходит, хотя ...

В C++ 11, things are simpler (хотя и не менее многословны), потому что вы можете обнаружить вещи использования непосредственно. Эквивалент черта таким образом:

template <typename T> 
class has_size { 
private: 
    typedef char Yes; 
    typedef Yes No[2]; 

    template<typename C> static auto Test(void*) 
    -> decltype(size_t{std::declval<C const>().size()}, Yes{}); 

    template<typename> static No& Test(...); 

public: 
    static bool const value = sizeof(Test<T>(0)) == sizeof(Yes); 
}; 

Однако рекомендуемый метод в C++ является не использовать черты если вы можете; в функциях, например, вы можете использовать decltype прямо в типе.

+0

К сожалению, я связан с C++ 98 здесь; могу ли я как-то подписать как 'size_t size()', так и 'size_t size() const'? – nijansen

+0

@nijansen: вы можете, но имеет ли это смысл? Обратите внимание, что если ваша функция 'void doSomething (C const & container)', тогда требуется использовать метод 'const' :). См. Отредактированный ответ. –

+0

Да, мне пришлось изменить это, передавая его по значению, потому что мне нужно использовать некоторые библиотеки, которые действительно не соответствуют константной корректности ... Я все еще хочу, чтобы мой метафайл has_size был совместим с этими библиотечными контейнерами и контейнеры STL – nijansen

1

Да:

char (*)[sizeof(&C::size)] 
+3

Это совершенно незавершенное. Если C не имеет метода «размер», он терпит неудачу (здесь нет SFINAE). Если C имеет член «размер», который не является методом, он также терпит неудачу (эта строка «работает», но выбирает неправильный элемент) – xryl669

Смежные вопросы