通过隐式转换返回时是否需要复制构造函数?

时间:2014-06-16 07:43:32

标签: c++ copy-constructor implicit-conversion copy-elision

以下代码在Visual C ++ 2013中编译良好,但不在GCC或Clang下编译。

哪个是正确的?
通过隐式转换返回对象时是否需要可访问的复制构造函数?

class Noncopyable
{
    Noncopyable(Noncopyable const &);
public:
    Noncopyable(int = 0) { }
};

Noncopyable foo() { return 0; }

int main()
{
    foo();
    return 0;
}

GCC

error: 'Noncopyable::Noncopyable(const Noncopyable&)' is private
  Noncopyable(Noncopyable const &);
  ^
error: within this context
 Noncopyable foo() { return 0; }

锵:

error: calling a private constructor of class 'Noncopyable'
Noncopyable foo() { return 0; }
                    ^
note: implicitly declared private here
        Noncopyable(Noncopyable const &);
        ^
warning: C++98 requires an accessible copy constructor for class 'Noncopyable' when binding a reference to a temporary; was private [-Wbind-to-temporary-copy]
Noncopyable foo() { return 0; }
                           ^
note: implicitly declared private here
        Noncopyable(Noncopyable const &);
        ^

3 个答案:

答案 0 :(得分:15)

当您return表达式时,将创建一个返回类型的临时对象,使用该表达式初始化,然后移动(或复制,如果移动不是一个选项),返回到返回值。所以你需要一个可访问的副本或移动构造函数。

然而,可以使用支撑列表直接初始化返回值。以下是有效的:

Noncopyable foo() { return {0}; }

live example中的类似案例。

答案 1 :(得分:5)

12.8复制和移动类对象[class.copy]

  

1 / 可以通过两种方式复制或移动类对象:初始化(12.1,8.5),包括函数参数   传递(5.2.2)和用于函数值返回(6.6.3); [...]

6.6.3返回语句[stmt.return]

  

2 / [...]表达式的值是隐含的   转换为它出现的函数的返回类型。 返回声明可能涉及到   构建和复制或移动临时对象的 (12.2)[...]

12.2临时对象[class.temporary]

  

1 / 在各种上下文中创建类类型的临时数:绑定对prvalue的引用(8.5.3),返回   prvalue(6.6.3),一个创建prvalue的转换(4.1,5.2.9,5.2.11,5.4),[...]注意:即使没有调用析构函数或副本/ move构造函数,全部   语义限制,如可访问性(第11条)和功能是否被删除(8.4.3),应   满意。 [...]

我认为GCC和clang是正确的 - 我甚至会说,无论何时按值返回,返回类型都必须有一个可访问的副本或移动构造函数。

逻辑是创建临时文件以将原始类型转换为新类型(intNoncopyable),然后为该函数返回该临时文件的副本。

它的基本相同:

Noncopyable foo() { return Noncopyable(0); }

您是否希望在那里需要副本?我当然会。

答案 2 :(得分:4)

  • 函数foo按值返回Noncopyable个对象。因此,理论上必须引发复制构造函数。

  • 如果您使复制构造函数可用(即public)并打印一条消息来标记其召唤,您将看到此消息未打印DEMO且只有重载引起转换操作符。

  • 这是由于复制省略优化的原因。

  • 因此,重载转换运算符不需要复制构造函数,而是foo的return语句需要复制构造函数,因为您按值返回。

  • 最终,复制构造函数不会因复制省略而被引发,但仍然必须可用。