Reputation: 26896
I'm trying to write a variation of the template class defining a super
type idiom. The class Inherit
introduces the type Super
to denote the possibly very long super type, and also needs to know the derived type New
to do some extra things which I'm not showing here.
This works fine if the type passed to New
is not a template, but fails with templates. Here is a full example compiled with clang++-3.8 -std=c++1y -Wall
(gcc gives the same output):
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;
}
If I didn't need the New
parameter I am also able to use Inherit
with templates, but I really need New
. I could make the code compile by using option D
but that defeats the whole purpose.
I have two questions:
Super
from being known inside the class Template
and why is that different in the NonTemplate
case?Upvotes: 2
Views: 1429
Reputation: 65700
The issue is that Inherit<Template<T>, SomeBase>
is a dependent base class, i.e. the class depends on a template parameter. Names in dependent base class are hidden from unqualified lookup, so you need to qualify the name.
typename
is needed because Inherit<Template<T>, SomeBase>::Super
is a dependent name and the compiler needs you to tell it that it names a type, as it can only be certain at instantiation time.
See Where and why do I have to put the “template” and “typename” keywords? for an in-depth explanation of the typename
keyword.
As for solutions, you could factor Super
out into a type trait:
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>()));
This makes usage much simpler:
template<class T>
struct Template : Inherit<Template<T>, SomeBase>
{
using D = Super<Template>;
};
If you find yourself needing this for multiple base classes, you could generalise it:
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>;
};
Upvotes: 6