在VS2013更新5中,我有这个:
class Lock
{
public:
Lock(CriticalSection& cs) : cs_(cs)
{}
Lock(const Lock&) = delete;
Lock(Lock&&) = delete;
Lock& operator=(const Lock&) = delete;
Lock& operator=(Lock&&) = delete;
~Lock()
{
LeaveCriticalSection(&(cs_.cs_));
}
private:
CriticalSection& cs_;
};
class CriticalSection
{
CRITICAL_SECTION cs_;
public:
CriticalSection(const CriticalSection&) = delete;
CriticalSection& operator=(const CriticalSection&) = delete;
CriticalSection(CriticalSection&&) = delete;
CriticalSection& operator=(CriticalSection&&) = delete;
CriticalSection()
{
InitializeCriticalSection(&cs_);
}
~CriticalSection()
{
DeleteCriticalSection(&cs_);
}
// Usage: auto lock = criticalSection.MakeLock();
Lock MakeLock()
{
EnterCriticalSection(&cs_);
return Lock(*this);
}
}
MakeLock
返回不可移动,不可复制类型的实例。这似乎工作正常。但是,Visual Studio intellisense确实强调了红色的返回,并警告说Lock的移动构造函数不能被引用,因为它是一个已删除的函数。
我试图理解为什么这有效,如果它是标准符合C ++或只是MSVC特有的东西。我猜回归是有效的,因为构建返回值的需要可以被优化掉,所以intellisense警告警告一些事实上没有 - 实际上 - 实际发生。
我想我在某处读到C ++会标准化以确保返回值优化总是会发生。
那么,这个符合C ++的代码是否会继续在未来的编译器中运行?
P.S。我意识到std::mutex
和std::lock_guard
可能会取代这个。
答案 0 :(得分:8)
如果编译,则编译器中存在错误。 VC2015无法正确编译它。
class Foo
{
public:
Foo() {}
Foo(const Foo&) = delete;
Foo(Foo&&) = delete;
};
Foo Bar()
{
return Foo();
}
给我:
xxx.cpp(327): error C2280: 'Foo::Foo(Foo &&)': attempting to reference a deleted function
和g ++ 4.9说:
error : use of deleted function 'Foo::Foo(Foo&&)'
标准非常清楚,复制构造函数或移动构造函数必须存在且可访问,即使RVO意味着它未被调用。
答案 1 :(得分:5)
在C ++ 17中,Martin Bonner的答案中的代码是合法的。
不仅允许编译器,而且有责任删除副本。 Clang和GCC的实时示例。 C ++ 17 6.3.2 / 2(强调我的意思):
[...] [注意:return语句可能涉及调用构造函数以执行操作数的复制或移动,如果它不是prvalue 或它的类型不同于函数的返回类型。如果返回了自动存储持续时间变量,则可以取消与return语句关联的复制操作或将其转换为move操作(10.9.5)。 —尾注]
这里的prvalue意味着一个临时值。有关确切的定义和大量示例,请参见here。
在C ++ 11中,这确实是非法的。但这很容易解决,通过在return语句中使用括号初始化,您可以在调用站点位置构造返回值,从而完全绕开了经常被淘汰的复制构造函数的要求。 C ++ 11 6.6.3 / 2:
[...]带有括号初始化列表的return语句通过从指定的初始化程序列表进行复制列表初始化(8.5.4)初始化要从函数返回的对象或引用。 [...]
复制列表初始化意味着只调用构造函数。不涉及复制/移动构造函数。
不幸的是,它们都不能在任何recent version of the Visual Studio compiler中工作,这很可悲,并且其不良C ++一致性的声誉很高。像这样返回不可复制的对象对于返回例如功能中的std::lock_guard
个(允许您轻松地将std::mutex
成员设为私有)等。
答案 2 :(得分:0)
从MSVC v19.14开始,MSVC(aka Visual Studio)还实现了C ++ 17行为,在适用RVO的情况下,允许return
不可复制,不可移动的对象:{{ 3}}
正如rubenvb的回答所表明的,这意味着GCC,Clang和MSVC都支持以下C ++ 17行为:https://godbolt.org/z/fgUFdf