代码之家  ›  专栏  ›  技术社区  ›  Michael Wild

C++中派生类中私有复制构造函数的对象切片

  •  2
  • Michael Wild  · 技术社区  · 16 年前

    假设我有如下的东西 CXX (我做物体切片 故意):

    class A {
    };
    
    class B : public A {
      // prevent copy construction and assignment
      B(const B& other);
      B& operator=(const B& other);
    public:
      explicit B(){}
    };
    
    class C {
      A m_a;
    public:
      explicit C() : m_a( B() ) {} // 1
    };
    

    我希望这能奏效,就像 应该调用类A的复制构造函数(这里是编译器生成的和公共的)。此代码在最近的编译器(我尝试了G++-4.4和Intel 11.0)上也能很好地编译,但是较旧的编译器(如G++-4.2和G++-4.0)尝试调用B的复制构造函数,我声明它是私有的,结果是:

    test.cxx: In constructor ‘C::C()’:
    test.cxx:7: error: ‘B::B(const B&)’ is private
    test.cxx:16: error: within this context

    现在,在我的构建系统中,我想检查编译器是否支持上述代码。然而,问题是,这个标准是否符合规范?这种测试的正确名称是什么?

    编辑 :对不起,英特尔编译器10.1版和11.0版都发布了以下内容: 警告734:“B::B(const B&)”(在第6行声明),已删除的副本需要,不可访问

    5 回复  |  直到 16 年前
        1
  •  4
  •   avakar    16 年前

    在这种情况下,我敢不同意科莫的观点。实际上,以下代码未能按预期编译,因为将右值绑定到常量引用需要可访问的复制构造函数。

    class A {
    };
    
    class B : public A {
      B(const B& other);
      B& operator=(const B& other);
    public:
      explicit B(){}
    };
    
    int main()
    {
        A const & a = B();
    }
    

    根据8.5.3/2,“[…]传递的参数(5.2.2)和函数值返回(6.6.3)是初始化,”因此,代码的格式应不正确。

    编辑: 我仍然坚信,代码是根据C++ 03的错误格式形成的。不过,我刚刚阅读了C++ 0x工作草案的相关部分,看来它不再需要复制构造函数可用了。也许这就是从GCC-4.2迁移到GCC-4.3时代码开始编译的原因。

    编辑: 为了澄清原因 B::B(const B &) 必须可访问是由于 B() 到的第一个参数 A::A(const A &) (当然,这是在 m_a 正在初始化)。

    编辑: 关于C++ 03和C++ 0x的区别,LITB很好地找到了 relevant defect report .

        2
  •  1
  •   Michael Wild    16 年前

    如果没有可用的复制构造函数,则旧的g++编译器似乎无法通过引用传递临时对象:

    class A { 
      A(const A& other);
      A& operator=(const A& other);
    public:
      explicit A(){}
    };
    void f( const A& a ) {}
    int main() {
      A a;
      f( a );    // fine
      f( A() );  // fails
    }
    
        3
  •  1
  •   Fernando N.    16 年前

    我认为它是C++中的标准一致代码,而不是C++ 03。

    我将测试命名为“从右值复制构造”。

    据报道这是一个错误,但海湾合作委员会的人认为 here 这是正确的行为,并提供了标准的参考。

    [DCL.初始参考]/5,项目符号2,子项目符号1

    如果初始值设定项表达式位于 右值,t2 a类类型和“cv1 T1“与CV2参考兼容 t2“引用绑定在 以下方式(选择是 定义的实现):
    -引用绑定到由右值表示的对象(请参见 3.10)或该对象内的子对象。
    -创建“cv1 t2”[sic]类型的临时,构造函数是 调用以复制整个右值 对象转换为临时对象。这个 引用绑定到临时或 到临时对象中的子对象。

    将用于 制作副本 应可调用 拷贝是否真实 完成 .

    C++0x标准消除了歧义,而引用总是与由rValo表示的对象绑定,不需要构造函数是可访问的。

        4
  •  0
  •   anon    16 年前

    我不能给你指出标准的某个特定部分,但我认为它是可以接受的,并与 Comeau C++ 以及编译程序 提到。至于这样一个测试的名称,我想“编译器兼容性”和其他测试一样好。

        5
  •  0
  •   Sam    16 年前

    这是有效的。调用b()构造一个b对象;调用b的默认编译器生成的公共构造函数。

    构造C对象时,将构造一个对象。因为它是一个值,所以将考虑静态类型,并且将对从派生并用于复制构造对象的任何对象进行切片。类A具有编译器提供的公共副本构造函数。编译器发现B对象也是A类型的。编译器只关心复制构造A对象,它知道可以这样做,所以它使用复制构造函数将B对象中的A对象复制到C::M_A对象。ie由于静态类型而切片。

    观察这些物体的记忆是有指导意义的。将char*数据成员放在初始化为“i'm a”的中,将char*数据成员放在初始化为“i'm a b”的中,并单步执行调试器以监视对象。你应该足够容易地看到这些线。