Как работает этот шаблон класса has_member?

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

template <typename Type> 
class has_member 
{ 
   class yes { char m;}; 
   class no { yes m[2];}; 
   struct BaseMixin 
   { 
     void operator()(){} 
   }; 
   struct Base : public Type, public BaseMixin {}; 
   template <typename T, T t>  class Helper{}; 
   template <typename U> 
   static no deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0); 
   static yes deduce(...); 
public: 
   static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); 
};

Точнее, я не понимаю назначение BaseMixin и наличие в нем operator(). Кроме того, поскольку Base происходит от него, я тоже этого не понимаю.

Более конкретно, когда параметр шаблона Type определил operator(), почему только запускается SFINAE, в результате чего первая функция deduce() игнорируется и выбирается вторая?


Во всяком случае, это мой тестовый код:

struct A{};                             //SFINAE is triggered for A
struct B{ void operator()(){} };        //SFINAE is not triggered for B
struct C{ void operator()(int,int){} }; //SFINAE is not triggered for C

int main() 
{       
   std::cout << std::boolalpha; //enable true/false instead of 1/0!
   std::cout << has_member<A>::result << std::endl;
   std::cout << has_member<B>::result << std::endl;
   std::cout << has_member<C>::result << std::endl;
}

Вывод(ideone):

false
true
true

person Nawaz    schedule 02.02.2012    source источник


Ответы (2)


  • BaseMixin имеет определение operator().
  • Base происходит как от Type, так и от BaseMixin, поэтому, если Type имеет operator(), поиск имени в Base::operator() будет неоднозначным.
  • deduce вызывается для Base*, а не Type*.
  • Helper<void (BaseMixin::*)(), &U::operator()> будет создан только в том случае, если &U::operator() однозначно разрешается в BaseMixin::operator().
  • И наоборот, если Helper<void (BaseMixin::*)(), &U::operator()> не создает экземпляр, это связано с тем, что Type имеет собственный operator() поиск имени в &U::operator() неоднозначном, и, следовательно, выбрана перегрузка deduce, возвращающая тип yes.

Стандартная цитата относительно второй пули С++ 11 §10.2/2-6:

2 Следующие шаги определяют результат поиска имени члена f в области класса C.

3 Набор поиска для f в C, называемый S(f,C), состоит из двух наборов компонентов: набор объявлений, набор членов по имени f; и набор подобъектов, набор подобъектов, в которых были найдены объявления этих членов (возможно, включая объявления использования). В наборе объявлений объявления-использования заменяются членами, которые они обозначают, а объявления типов (включая имена внедренных классов) заменяются типами, которые они обозначают. S(f,C) рассчитывается следующим образом:

4 Если C содержит объявление имени f, набор объявлений содержит каждое объявление f, объявленное в C, которое удовлетворяет требованиям языковой конструкции, в которой происходит поиск. [ Примечание. При поиске имени в уточненном-спецификатор-типе или базовом-спецификаторе, например, игнорируются все объявления, не относящиеся к типу, при поиске имени в вложенном спецификаторе имени игнорируются объявления функций, переменных и перечислителей. В качестве другого примера, поиск имени в using-declaration включает объявление класса или перечисления, которое обычно скрыто другим объявлением этого имени в той же области. конец примечания ] Если результирующий набор объявлений не пуст, набор подобъектов содержит сам C, и расчет завершен.

5 В противном случае (т. е. C не содержит объявления f или результирующий набор объявлений пуст), S(f,C) изначально пуст. Если C имеет базовые классы, вычислите набор поиска для f в каждом подобъекте прямого базового класса Bi и объедините каждый такой набор поиска S(f ,Bi), в свою очередь, в S(f,C).

6. Следующие шаги определяют результат объединения набора поиска S(f,Bi) с промежуточным S(f,C):

  • Если каждый из элементов подобъекта S(f,Bi) является подобъектом базового класса хотя бы одного из элементов подобъекта S(f,C ) или, если S(f,Bi) пусто, S(f,C) не изменяется, а слияние завершено. И наоборот, если каждый из элементов подобъекта S(f,C) является подобъектом базового класса хотя бы одного из элементов подобъекта S(f,Bi) или, если S(f,C) пусто, новый S(f,C) является копией S( f,Bi).
  • В противном случае, если наборы объявлений S(f,Bi) и S(f,C) различаются, слияние неоднозначно: новый S(f,C) представляет собой набор поиска с недопустимым набором объявлений и объединением наборов подобъектов. При последующих слияниях недопустимый набор объявлений считается отличным от любой другой.
  • В противном случае новый S(f,C) представляет собой набор поиска с общим набором объявлений и объединением наборов подобъектов.
person ildjarn    schedule 02.02.2012
comment
Пожалуйста, процитируйте спецификацию, чтобы поддержать это: Base сначала происходит от Type, затем от BaseMixin (так что, если у Type есть оператор(), который скроет/затенит тот, что в BaseMixin). Я хочу увидеть это сам. - person Nawaz; 02.02.2012
comment
@Nawaz: Прочитав стандартную цитату пару раз, я понял, что мое обоснование было неверным. Еще раз отредактировал. - person ildjarn; 02.02.2012

Base может наследовать operator() от Type или от BaseMixin, и если Type имеет operator(), он скрывает оператор BaseMixin.

Таким образом, если Type не имеет определенного operator (), operator() Base может быть неявно приведено к operator() BaseMixin. Тогда deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0) с U==Base совпадут.

И наоборот, если Type имеет operator(), то operator() Base не может быть приведено к BaseMixin, поэтому deduce(U*, Helper<void (BaseMixin::*)(), &U::operator()>* = 0) не будет соответствовать.

person Lol4t0    schedule 02.02.2012