2

When I write A::B::C, with A being a class, and B being its base class, I assume I'm accessing C which is defined in that base B. This wouldn't work when B isn't actually a base of A. However, the same apparently isn't true when B is a template, e.g. A::B<123>::C still gives me B<123>::C, and it doesn't seem to matter whether B<123> is actually a base of A or not. I'm at a loss why there could be a difference. Does it not interpret A::B<123> as accessing base class B<123> of class A? Why not? Can it be rewritten somehow so it does interpret it like accessing a base class?

Here's a snippet explaining what's done in detail, with comments explaining every step:

// Here we show that we can't access a non-existent base of A
namespace WorksAsExpectedWithoutTemplates {

struct B
{
  using C = void;
};

struct D
{
  using C = void;
};

struct A: B
{
};

// Compiles as expected, B is a base of A, so Foo is B::C, aka void
using Foo = A::B::C;

// Doesn't compile, as expected, because D isn't a base of A, even though D::C
// exists
using Bar = A::D::C; // WE DON'T EXPECT THIS TO COMPILE, WHICH IS FINE
}

// Now we try the same thing with templates, and it doesn't behave the same way.
// Why?
namespace ActsDifferentlyWithTemplates {

template< int >
struct B
{
  using C = void;
};

struct A: B< 42 >
{
};

// Compiles as expected, B< 42 > is a base of A, so Foo is B< 42 >::C, aka void
using Foo = A::B< 42 >::C;

// Compiles, Bar is B< 123 >::C, even though B< 123 > is not a base of A. Why
// does this behave differently than in the non-template case above? Can this be
// rewritten differently so that this wouldn't compile, same as in
// WorksAsExpectedWithoutTemplates, since B< 123 > isn't a base of A?
using Bar = A::B< 123 >::C; // WHY DOES THIS COMPILE? B< 123 > isn't a base of A
}
1
  • A::B<123>::C is interpreted as A::template B<123>::C. Commented Apr 14, 2022 at 4:17

1 Answer 1

2

template< int > struct B has an injected-class-name B that acts as a template name if it immediately precedes a <. The class struct A inherits this.

So, A::B< 123 >::C is the same as B< 123 >::C, not the base class B< 42 >. For example:

template<int X>
struct B {
    using C = char[X];
};

struct A : B<42> {};

using Foo = A::B<42>::C;
using Bar = A::B<123>::C;
using Baz = A::B::C;  // (injected-class-name without template)

static_assert(sizeof(Foo) == 42);
static_assert(sizeof(Bar) == 123);  // This is a different type
static_assert(sizeof(Baz) == 42);

None of these cases actually are "accessing a base class". They are all inheriting the injected-class-name from the base class like any other member type alias / member template.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.