条件虚拟继承和对虚拟基成员的访问
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 {};
到目前为止,我希望类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>::type
和std::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->str
或A::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)
}
};
- 虚拟成员函数的定义是否强制在同一转换单元中动态初始化静态数据成员?
- 钻石继承虚拟成员铸造与指针
- 如果我必须覆盖非虚拟成员函数怎么办
- c++\CLI dll包装器,用于调用c++类中的虚拟成员
- 非虚拟成员函数是否可以使用模板参数?
- 如何将已实现的虚拟成员函数作为参数传递
- 调用虚拟成员类的方法
- 睡眠影响 std::thread 调用哪个虚拟成员函数?
- GTEST:嘲笑非虚拟成员函数
- 使SDL称为纯虚拟成员作为事件回调
- 在C中使用具有虚拟成员(即非POD)的C++结构
- 虚拟和非虚拟成员函数的调用方式有什么区别?
- 默认情况下是虚拟成员函数
- 是否可以在不重写派生类中记录虚拟成员?
- 为什么在已删除的指针上调用非虚拟成员函数是未定义的行为
- C++标准是否保证了没有虚拟成员函数的类类型的大小
- 使用虚拟成员从课堂制作 POD
- C++合成的move构造函数如何受到volatile和虚拟成员的影响
- 指向虚拟成员功能
- 在非构造"object"上调用非虚拟成员函数是否定义良好?