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

时间:2015-09-26 13:14:07

标签: c++ inheritance virtual-inheritance

让我们看看下面的代码:

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)没有任何问题。

如果使用条件继承,是否有任何理由无法访问虚拟基本成员?或者它是某种编译器错误?

2 个答案:

答案 0 :(得分:2)

这不是编译器错误。在您的示例中,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

  

在类或类模板的定义中,在类模板或成员的定义点或在实例化实例化期间,在非限定名称查找期间不检查从属基类(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
};
     

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

GCC和Clang正在发出一个str未声明的错误,因为编译器实际上是在str声明之前尝试在全局范围内找到X的声明。 / p>

您只需撰写strthis->str即可获得A::str资格。

答案 1 :(得分:1)

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