Artur Pyszczuk
Artur Pyszczuk

Reputation: 1940

Conditional virtual inheritance and access to virtual base member

Lets take a look at the code below:

struct A {
    A (std::string s) : str {s} {}
    A () = default;
    std::string str {"XXX"};
};

struct B1 : virtual A {
    B1 () = default;

    void foo_b1 () {
        std::cout << str << std::endl;
    }
};

struct B2 : virtual A {
    B2 () = default;

    void foo_b2 () { 
        std::cout << str << std::endl;
    }
};

struct Empty {};

So far so good, I want class A to have a member which instance (one and only one) will be shared between two classes B1 and B2, so I used virtual inheritance as it is. Next step is conditional inheritance from B1 and B2, depending on the template parameter T, like this:

template <typename T>
struct X : std::conditional<std::is_integral<T>::value, B1, Empty>::type,
           std::conditional<std::is_floating_point<T>::value, B2, Empty>::type {
    X () : A ("X ctor") {
//        std::cout << str << std::endl;  // (X)
    }
};

Everything is fine and usage of class X is almost as I wanted, so I am able to do this:

X<int> x1;
x1.foo_b1 ();
x1.foo_b2 ();   // (1)

X<double> x2;
x2.foo_b1 ();   // (2)
x2.foo_b2 ();

Lines (1) and (2) of course don't compile and this is what I wanted, but if I uncomment line (X) GCC 4.8.3 and Clang 3.5.0 reject the code with message:

error: ‘str’ was not declared in this scope`  # GCC
error: use of undeclared identifier 'str'`    # Clang

Why? I inherited from B1 or B2, I should have access to virtual base member. So I tested it without conditional inheritance.

template <typename T>
struct Z : B1, Empty {
    Z () : A ("Z ctor") {
        std::cout << str << std::endl;    // (Y)
    }
};

With usage like this (of course template parameter is unnecessary here. Class Z itself behaves like X<int> specialization):

Z<int> z;
z.foo_b1 ();

And this is completely fine for both compilers. Line (Y) does not make any trouble in this case.

Is there any reason why there is no access to virtual base member if conditional inheritance is used? Or is it some kind of compilers bug?

Upvotes: 2

Views: 193

Answers (2)

Daniel Trebbien
Daniel Trebbien

Reputation: 39208

This is not a compiler bug. In your example, both std::conditional<std::is_integral<T>::value, B1, Empty>::type and std::conditional<std::is_floating_point<T>::value, B2, Empty>::type are dependent base classes because they depend on the template parameter T. As specified in §14.6.2 of the current working draft, n4527:

In the definition of a class or class template, the scope of a dependent base class (14.6.2.1) is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [ Example:

typedef double A;
template<class T> class B {
    typedef int A;
};
template<class T> struct X : B<T> {
    A a; // a has type double
};

The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>. — end example ]

GCC and Clang are properly issuing an error that str is undeclared because the compiler is essentially trying to find a declaration of str in the global scope before the declaration of X.

You simply need to qualify str, either by writing this->str or A::str.

Upvotes: 2

Jarod42
Jarod42

Reputation: 217458

str is a dependent name and so you have to use this->str (or A::str).

or else you have to inherit directly from A:

template <typename T>
struct X : virtual A,
           std::conditional<std::is_integral<T>::value, B1, Empty>::type,
           std::conditional<std::is_floating_point<T>::value, B2, Empty>::type {
    X () : A ("X ctor") {
        std::cout << str << std::endl;  // (X)
    }
};

Upvotes: 1

Related Questions