2016-02-05 6 views
1

Я пытаюсь написать вариант template class defining a super type idiom. Класс Inherit вводит тип Super, чтобы обозначить, возможно, очень длинный супер-тип, а также должен знать производный тип New, чтобы сделать некоторые дополнительные вещи, которые я здесь не показываю.C++ не может найти тип, определенный в базовом классе шаблона, который наследует от текущего класса шаблона

Это работает отлично, если тип, переданный в New, не является шаблоном, но не с шаблонами. Вот полный пример компилируется с clang++-3.8 -std=c++1y -Wall (НКУ дает тот же результат):

struct SomeBase {}; 

template<class New, class Base> 
struct Inherit : Base { 
    using Super = Inherit<New, Base>; 
}; 

struct NonTemplate : Inherit<NonTemplate, SomeBase> { 
    using X = Super; 
    // compiles and is nice and short 
}; 

template<class T> 
struct Template : Inherit<Template<T>, SomeBase> 
{ 
    using A = Super; 
    // error: unknown type name 'Super'; did you mean 'NonTemplate::Super'? 

    using B = typename Super; 
    // error: expected a qualified name after 'typename' 

    using C = Inherit::Super; 
    // error: 'Inherit' is not a class, namespace, or enumeration 

    using D = typename Inherit<Template<T>, SomeBase>::Super; 
    // compiles, but what's the point? 
}; 

int main() { 
    return 0; 
} 

Если мне не нужен параметр New Я также возможность использовать Inherit с шаблонами, но мне действительно нужно New. Я мог бы скомпилировать код, используя опцию D, но это побеждает всю цель.

У меня есть два вопроса:

  1. Что именно проблема языка, который предотвращает имя типа Super от известности внутри класса Template и почему это отличается в NonTemplate случае?
  2. Есть ли у кого-нибудь идеи для хорошего решения этой проблемы?
+0

В зависимости от названия ... 'Inherit , SomeBase>' может иметь некоторую специализацию, которая может изменить значение 'Super'. – Jarod42

ответ

6

Вопрос заключается в том, что Inherit<Template<T>, SomeBase> является зависимым базовым классом, то есть класс зависит от параметра шаблона. Имена в зависимом базовом классе скрыты от неквалифицированного поиска, поэтому вам нужно квалифицировать имя.

typename необходимо потому, что Inherit<Template<T>, SomeBase>::Super является зависимым именем, и компилятору необходимо сообщить ему, что он называет тип, поскольку он может быть определен только во время создания экземпляра.

См. Where and why do I have to put the “template” and “typename” keywords? для подробного объяснения ключевого слова typename.


Что касается решений, то вы могли бы фактор Super вне в тип признака:

template<class New, class Base> 
struct Inherit : Base { 
}; 

namespace detail { 
    template <class New, class Base> 
    Inherit<New,Base> Super (const Inherit<New, Base>&); 
} 
template <class T> 
using Super = decltype(detail::Super(std::declval<T>())); 

Это делает использование гораздо проще:

template<class T> 
struct Template : Inherit<Template<T>, SomeBase> 
{ 
    using D = Super<Template>; 
}; 

Если вы обнаружили, нуждающихся в этом для нескольких базовых классов, вы можете его обобщить:

namespace detail { 
    template <template <typename...> class T, class... Args> 
    T<Args...> Super (const T<Args...>&); 
} 
template <template <typename...> class Base, class Derived> 
using Super = decltype(detail::Super<Base>(std::declval<Derived>())); 

template <typename T> 
using InheritSuper = Super<Inherit,T>; 

template<class T> 
struct Template : Inherit<Template<T>, SomeBase> 
{ 
    using D = InheritSuper<Template>; 
}; 
+0

Спасибо за широкое решение! :) Я бы хотел, чтобы эта сложность не нуждалась, так как это делает использование более сложным ('Super' vs' Super '), но все же лучше, чем указывать полный базовый тип. –