以下代码在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 &);
^
答案 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是正确的 - 我甚至会说,无论何时按值返回,返回类型都必须有一个可访问的副本或移动构造函数。
逻辑是创建临时文件以将原始类型转换为新类型(int
到Noncopyable
),然后为该函数返回该临时文件的副本。
它的基本相同:
Noncopyable foo() { return Noncopyable(0); }
您是否希望在那里需要副本?我当然会。
答案 2 :(得分:4)
函数foo
按值返回Noncopyable
个对象。因此,理论上必须引发复制构造函数。
如果您使复制构造函数可用(即public
)并打印一条消息来标记其召唤,您将看到此消息未打印DEMO且只有重载引起转换操作符。
这是由于复制省略优化的原因。
因此,重载转换运算符不需要复制构造函数,而是foo
的return语句需要复制构造函数,因为您按值返回。
最终,复制构造函数不会因复制省略而被引发,但仍然必须可用。