禁止复制构造函数,但允许其他类型的隐式复制

时间:2014-08-31 07:00:49

标签: c++ class c++11 copy-constructor

这是我的代码,我禁用了复制构造函数,但它也禁用了其他类型的隐式副本。在这种情况下有什么解决方法吗?

测试:g ++(GCC)4.7.1

struct item {
  int b;
};

class test {
 public:
  test(const test& copy) = delete;

  test(const item& a) {
    std::cout << "OK " << a.b << std::endl;
  }
};

int main() {
  test a = item{10}; //error: use of deleted function ‘test::test(const test&)’
}

2 个答案:

答案 0 :(得分:5)

test一个移动构造函数:

test(test&&) = default;

或使用直接初始化:

test a{item{10}};

没有其他解决方法。目标类型是类类型的复制初始化,例如test a = item{10};,总是需要可调用的副本或移动构造函数。


相关规则在§8.5[dcl.init] / p17:

中规定
  

如果目标类型是(可能是cv限定的)类类型:

     
      
  • 如果初始化是直接初始化,或者是复制初始化,那么cv-nonqualified版本的源   type与类的类相同,或者是类的派生类   目的地,建设者被考虑。适用的构造函数   列举(13.3.1.3),并通过过载选择最好的一个   决议(13.3)。如此选择的构造函数被调用以初始化   对象,使用初始化表达式或表达式列表作为其对象   参数(一个或多个)。如果没有构造函数适用,或者重载解析是   暧昧,初始化是不正确的。
  •   
  • 否则(即,对于剩余的复制初始化情况),可以从源转换的用户定义的转换序列   键入目标类型或(使用转换函数时)   按照13.3.1.4的描述列举其派生类别,   并且通过重载决策(13.3)选择最好的一个。如果   转换不能完成或模糊,初始化是   病态的。使用初始化程序调用所选的函数   表达作为其论点;如果函数是构造函数,则调用   初始化一个临时的cv-nonqualified版本的   目的地类型。临时是一个prvalue。通话的结果   (然后用于构造函数的临时情况)   根据上面的规则,直接初始化对象   复制初始化的目的地。在某些情况下,   允许实现消除此中固有的复制   通过直接构造中间结果直接初始化   进入被初始化的对象;见12.2,12.8。
  •   

源类型为item,目标类型为test,它是复制初始化,因此它属于第二个项目符号点。使用test(const item& a)构造函数只有一个可用转换,因此test构造了item类型的prvalue临时值,然后根据第一个项目符号点直接初始化目标。反过来,这必须调用test的构造函数,该构造函数可以接受const test &test &&参数。即使复制或移动被删除,您仍然必须有这样的构造函数。

答案 1 :(得分:0)

我可以想到三个选项:

1)移动构造函数
2)赋值运算符+默认构造函数
3)显式调用构造函数

#include <iostream>

struct item {
    int b;
};
struct test {
    test(const test& copy) = delete;
    test(const item& a) {
        std::cout << "OK " << a.b << std::endl;
    }
//  move:
    test(test&& from) {}
//  added:
    test() {}
    test& operator = (const test& src) = default;
};

int main() {
//fine after move constructor:
    test a = item{10};
//all fine with original
    test b(item{20});
//fine after adding .ctor() and op=
    test c; c = item{30};
}