【问题标题】:Conditional virtual inheritance and access to virtual base member有条件的虚拟继承和对虚拟基成员的访问
【发布时间】:2015-12-24 04:42:43
【问题描述】:

让我们看看下面的代码:

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&lt;int&gt;特化):

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

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

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

【问题讨论】:

  • "gcc 4.8.3 和 clang 3.5.0 拒绝代码" - 也可以包括在您的问题中发生拒绝时发出的逐字错误消息文本。 (请在您的问题中提出;您知道这是如何工作的)。
  • 猜测:条件继承使str 依赖于T,因此您可能必须使用this-&gt;str 才能访问它。编译器可能会考虑如果T 属于某种类类型会发生什么。
  • @BoPersson “条件继承使 str 依赖于 T,因此您可能必须使用 this->str 来访问它。”你说的对。这解决了问题。
  • 这本质上是“什么是从属名称”的副本,或者任何该问题的名称。

标签: c++ inheritance virtual-inheritance


【解决方案1】:

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

在类或类模板的定义中,在非限定名称查找期间,无论是在类模板或成员的定义点还是在实例化类模板或成员。 [ 例子:

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&lt;T&gt; 定义中的类型名称 A 绑定到全局命名空间范围中定义的 typedef 名称,而不是基类 B&lt;T&gt; 中定义的 typedef 名称。 — 结束示例 ]

GCC 和 Clang 正确地发出 str 未声明的错误,因为编译器本质上是试图在声明 X 之前在全局范围内找到 str 的声明。

您只需要通过编写this-&gt;strA::str 来限定str

【讨论】:

    【解决方案2】:

    str 是从属名称,因此您必须使用 this-&gt;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)
        }
    };
    

    【讨论】:

      猜你喜欢
      • 2012-09-27
      • 2023-03-23
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      • 1970-01-01
      • 2020-02-15
      相关资源
      最近更新 更多