Аргумент шаблона после оператора разрешения области не заменяется

Я пытаюсь создать общий код, который вызовет ошибку компиляции, если B не является предком D. Что я придумал:

template<typename B, typename D>
struct assert_base_of {
    enum {value = sizeof(B::D)};
}

Это не работает. Когда я «вызываю» это так:

assert_base_of<A2,A1>::value;

Я получаю следующую ошибку (g++ 4.8.2):

main.cpp:16:22: error: ‘D’ is not a member of ‘A2’

Похоже, параметр шаблона D не заменяется на A1. Может ли кто-нибудь объяснить и предложить решение, пожалуйста?


person binduck    schedule 20.06.2015    source источник
comment
Что не так с static_assert (std::is_base_of<B, D>::value, "B must be a base of D") ?   -  person Alexandre C.    schedule 20.06.2015
comment
Мне не разрешено использовать std::is_base_of, и даже если бы я мог, я все равно хочу знать, почему D не заменяется :)   -  person binduck    schedule 20.06.2015
comment
Разрешено кем? Если кто-то наложил произвольные ограничения на ваш возможный набор решений, вы должны указать их в вопросе, чтобы избежать потенциальной траты нашего времени.   -  person Lightness Races in Orbit    schedule 20.06.2015
comment
B::D интерпретируется как член (а не тип), потому что B здесь зависим. В любом случае, вы не можете получить такой базовый класс — вам нужно привести указатель от D к B, чтобы проверить, наследует ли D B. См. ответ @Veritas о том, как реализовать is_base_of.   -  person Alexandre C.    schedule 20.06.2015
comment
@АлександрК. Спасибо за ответ, но что значит «зависимый»? Есть ли способ переопределить это? а также при выполнении sizeof(A::B) вне шаблона моя программа компилировалась, когда B была основой A, и не компилировалась, когда это было не так. Что заставило его работать?   -  person binduck    schedule 20.06.2015
comment
B зависимый означает, что он зависит от параметра шаблона (здесь это является параметром шаблона). Когда вы пишете B::foo, foo всегда интерпретируется как имя члена B, если B является зависимым (это связано с тем, что компилятор дважды просматривает ваш шаблон, и в первом проходе он не знает, что в итоге будет B) . Если вы хотите, чтобы foo называл тип, вы должны использовать typename B::foo. Если вы хотите, чтобы foo назвал шаблон, вы должны использовать template B::foo.   -  person Alexandre C.    schedule 20.06.2015
comment
Также см. stackoverflow.com/questions/2910979 /how-does-is-base-of-work, если вам не разрешено использовать C++11 и вы хотите решение C++03.   -  person Alexandre C.    schedule 20.06.2015
comment
Кроме того, то, что вы наблюдаете в вашем случае sizeof(A::B) без шаблона, - это внедренное имя класса B в B (typenameB внедряется в B, и A наследует его - A::A тоже существует как тип). Это темный угол C++, которого вам следует избегать, поскольку он ведет себя по-разному с шаблонами. См. stackoverflow.com /questions/7025054/ и связанные вопросы. Здесь важно то, что A::B называет тип, а не элемент.   -  person Alexandre C.    schedule 20.06.2015


Ответы (1)


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

#include <iostream>
#include <type_traits>

template<typename Base, typename Derived,
       bool = std::is_same<Base, Derived>::value>
struct is_base_of_impl
{

  typedef typename std::remove_cv<Base>::type     no_cv_base;      
  typedef typename std::remove_cv<Derived>::type  no_cv_derived;


  template<typename _Up>
  static std::true_type test(no_cv_derived&, _Up);
  static std::false_type test(no_cv_base&, int);

  //Black Magic
  struct converter
  {
   operator no_cv_derived&();
   operator no_cv_base&() const;
  };

  static const bool value = decltype(test(converter(), 0))::value;
};

template<typename Base, typename Derived>
struct is_base_of_impl<Base, Derived, true>
{ 
    static const bool value = std::is_same<Base, Derived>::value; 
};

template<typename Base, typename Derived>
struct is_base_of
: public std::integral_constant<bool,
               is_base_of_impl<Base, Derived>::value>
{ };


struct A {};
struct B1 : A {};
struct B2 : A {};
struct C : B1, B2 {};

int main()
{
    std::cout << is_base_of<A, B1>::value << "\n";
    std::cout << is_base_of<B1, C>::value << "\n";
    std::cout << is_base_of<A, C>::value << "\n";
    return 0;
}

Для получения дополнительной информации взгляните на эти ссылки:

Как работает `is_base_of`?

https://groups.google.com/d/msg/comp.lang.c++.moderated/xv4VlXq2omE/--WAroYkW2QJ

person Veritas    schedule 20.06.2015
comment
Я могу ошибаться, но после тестирования я думаю, что ваше решение не работает для множественного наследования. Если A наследуется от B1 и B2, и оба наследуются от C (невиртуальное наследование), я получаю 0, когда я делаю is_base_of‹C ,Ценность. - person binduck; 20.06.2015
comment
@pyx: вы могли бы сделать is_base_of<C,B1>::value && is_base_of<B1, A>::value. Думаю, правильнее было бы назвать это is_direct_base_of. - person Lightness Races in Orbit; 20.06.2015
comment
@Veritas: Добро пожаловать в 1 000 представителей! - person Lightness Races in Orbit; 20.06.2015
comment
@АлександрК. Я просто редактировал ответ, это также то, что libstdС++ использует внутри. - person Veritas; 20.06.2015