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

Conditional virtual inheritance and access to virtual base member

本文关键字:虚拟 成员 访问 继承 条件      更新时间:2023-10-16

让我们看看下面的代码:

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

到目前为止,我希望类A具有一个成员,该成员的实例(一个且只有一个)将在两个类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)
    }
};

一切都很好,类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)
    }
};
像这样使用

(当然这里不需要模板参数)。类Z本身的行为类似于X<int>专门化):

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

这对于两个编译器来说都是完全没问题的。行(Y)在这种情况下没有任何问题。

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

这不是编译器错误。在您的示例中,std::conditional<std::is_integral<T>::value, B1, Empty>::typestd::conditional<std::is_floating_point<T>::value, B2, Empty>::type都是依赖基类,因为它们依赖于模板参数T。如当前工作草案n4527§14.6.2所述:

在类或类模板的定义中,在类模板或成员的定义点或类模板或成员的实例化期间进行非限定名查找时,不检查依赖基类(14.6.2.1)的作用域。(例子:

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

X<T>定义中的类型名A绑定到全局命名空间作用域中定义的类型名,而不是绑定到基类B<T>中定义的类型名。- 结束示例]

GCC和Clang正确地发出str未声明的错误,因为编译器实际上是试图在X声明之前在全局作用域中找到str的声明。

您只需要限定str,可以写成this->strA::str

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