Template base class accessible when inheriting from a specific specialization?
Asked Answered
M

1

14

The other day, I discovered that this was possible:

template <class T> struct base {};
struct derived: base<int> {};

int main()
{
    // The base class template is accessible here
    typename derived::base<double> x;

    // from the comments, even this works
    typename derived::derived::base<double>::base<int>::base<void> y;
}

I have no recollection of ever reading this on cppreference or in C++ tutorials, or this being exploited in clever template metaprogramming tricks (because I'm sure it can be). I have several questions:

  • Does this thing have a specific name?
  • Where is it documented in the C++ standard and on cppreference?
  • Is there any template metaprogramming trick exploiting this?
Magna answered 15/1, 2018 at 18:36 Comment(16)
Note that base<double> isn't a base class of derived<int>.Panoptic
Not sure what you're trying to accomplish here? There's an irrelevant namespace on base and template type on derived.Cribwork
@MarkRansom I don't think this is an attempt at accomplishing anything. Rather it's a curious observation. Where you would expect to need to qualify base<double> with the namespace space, in this example using derived<int> seems to accomplish the same thing.Panoptic
People are essentially accessing some nested type when using stuff like std::enable_if<>::type. Same thing. Upd: not only base, but self class is also accessible: so you can write like typename derived::derived::derived::derived::base<double>::base<double>::base<double>::base<double> x;Zoolatry
@MarkRansom Yeah, I'm not trying to accomplish anything (yet :p). It just seems very weird to me...Magna
Possible duplicate of What does C++ syntax “A::B:A {};” meanZoolatry
Isn't this a dupe? I feel like the fact at least that the derived can be repeated has been discussed multiple times on SO.Erasmoerasmus
@NirFriedman That's not really the point of the question though.Phelips
FYI, clang gives the following warning, which makes me think this isn't legal c++: <source>:7:57: warning: ISO C++ specifies that qualified reference to 'base' is a constructor name rather than a template name in this context, despite preceding 'typename' keyword [-Winjected-class-name] typename derived::derived::base<double>::base<int>::base<void> x; with an arrow pointing at the start of the last base.Erasmoerasmus
@NirFriedman Only on Clang 5.0 and above (Clang 4.x does not give any warning). Hopefully other compilers fix this soon.Dentate
The simpler version seems to throw out some of the utility here, in that base is no longer in a remote namespace.Desta
[temp.local], [class.qual]/2Girth
@Girth Why didn't you write an answer?Phelips
So you're surprised that the name base is defined in the context of derived? I never thought about it but it makes sense that it would be. What really surprises me is @NirFriedman's note that it names a constructor function and not a type; normally constructors can't be called by name.Cribwork
@MarkRansom I don't know what's going on here, the rabbit hole seems pretty deep to me: godbolt.org/g/N2PFv6.Erasmoerasmus
@NirFriedman That warning is unjustified, because in a typename specifier, functions are ignored (IIRC).Fungiform
G
5

As pointed out by @Nir Friedman in comment, typename derived::derived::base<double>::base<int>::base<void> y; might actually be ill-formed because derived::derived::base<double>::base<int>::base is treated as a constructor of base, per [class.qual]/2.


  • Does this thing have a specific name?

It is called injected-class-name.

  • Where is it documented in the C++ standard and on cppreference?

In the standard: [class]/2 specifies that the name of a class is treated as if it were a public member of that class. [temp.local] specifies that the injected-class-name of a class template can be used as either a template-name or a type-name.

On cppreference: it is (incompletely) documented in http://en.cppreference.com/w/cpp/language/unqualified_lookup#Injected_class_name.

  • Is there any template metaprogramming trick exploiting this?

I'm not aware of any such tricks, though in everyday use, injected-class-name is employed whenever the current class is named in the class definition:

template<class T>
struct A {
    A<T>& operator=(const A<T>&); // injected-class-name as template-name
    A& operator=(A&&); // injected-class-name as type-name
};

The latter may be deliberately used to shorten member declaration.

The injected-class-name of a base class is mostly used (unconsciously) in a member initializer list:

struct B : A<int> {
    B() : A() {}
};
Girth answered 17/1, 2018 at 6:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.