有条件的虚拟继承和对虚拟基础成员的访问

Conditional virtual inheritance and access to virtual base member

让我们看看下面的代码:

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 {};

到目前为止一切顺利,我希望 class A 有一个实例(一个且只有一个)将在两个 classes B1 和 B2 之间共享的成员,所以我使用了虚拟继承事实上。下一步是从 B1 和 B2 条件继承,取决于模板参数 T,像这样:

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)
    }
};

一切正常, class X 的用法几乎如我所愿,所以我可以这样做:

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

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

第 (1) 和 (2) 行当然不会编译,这就是我想要的,但是如果我取消注释行 (X) GCC 4.8.3 和 Clang 3.5.0 拒绝带有消息的代码:

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

为什么?我继承自 B1 或 B2,我应该可以访问虚拟基础成员。所以我在没有条件继承的情况下进行了测试。

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

像这样使用(当然模板参数在这里是不必要的。Class Z 本身表现得像 X<int> 专业化):

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

这对两个编译器来说都完全没问题。在这种情况下,行 (Y) 不会造成任何麻烦。

如果使用条件继承,是否有任何原因无法访问虚拟基成员?还是某种编译器错误?

str 是从属名称,因此您必须使用 this->str(或 A::str)。

否则你必须直接继承自 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)
    }
};

这不是编译器错误。在您的示例中,std::conditional<std::is_integral<T>::value, B1, Empty>::typestd::conditional<std::is_floating_point<T>::value, B2, Empty>::type 都是从属基础 类,因为它们依赖于模板参数 T。如当前工作草案的§14.6.2 中所述,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 和 Clang 正确地发出了 str 未声明的错误,因为编译器本质上是试图在声明 X 之前在全局范围内找到 str 的声明.

您只需要通过写 this->strA::str.

来限定 str