在下面的代码中,我想移动构造一个没有move-constructor的对象:
class SomeClass{
public:
SomeClass() = default;
SomeClass(const SomeClass&) = default;
SomeClass( SomeClass&&) = delete;
};
SomeClass& getObject(){
return some_obj;
};
//...
SomeClass obj = std::move( getObject());
编译器给出错误:“使用已删除的函数”。这一切都很好。
另一方面,如果它有一个move-constructor但getObject()返回一个const对象,那么将调用复制构造函数,即使我试图用std :: move移动它。
是否有可能使编译器发出警告/错误,std :: move将无法生效,因为无法移动对象?
class SomeClass{
public:
SomeClass() = default;
SomeClass(const SomeClass&) = default;
SomeClass( SomeClass&&) = default;
};
const SomeClass& getObject(){
return some_obj;
};
//...
SomeClass obj = std::move( getObject());
答案 0 :(得分:1)
如果您的担心只是确定您获得了最佳性能,那么大多数时候您应该只信任编译器。事实上,您几乎不需要使用std::move()
。例如,在上面的示例中,它没有效果。现代编译器可以在移动发生时制定出来。
如果您的类应始终移动且永不复制,则删除其复制构造函数。
但也许你正在编写一个模板函数,如果你把它传递给一个没有移动构造函数的类,或者你还处理其他一些我没有想过的情况,那么它会有很糟糕的表现。在这种情况下,std::is_move_constructible
是您可能想要的。试试这个:
#include <type_traits>
#include <boost/serialization/static_warning.hpp>
template<class T>
T &&move_or_warn(T &t)
{
BOOST_STATIC_WARNING(std::is_move_constructible<T>::value);
return std::move(t);
}
现在,如果你执行SomeClass obj = std::move_or_warn( getObject());
,如果无法移动对象,则应该收到编译器警告。 (虽然我可能会使用普通std::move
并单独调用std::is_move_constructible
。)
不幸的是,C ++没有(还)有一种标准的方法可以产生你正在寻找的那种程序员指定的警告,这就是为什么我不得不使用boost。有关生成警告的更多讨论,请查看here。
答案 1 :(得分:1)
问题来自const
rvalue。此类参数与const T&
的匹配优于T&&
。如果你真的想要禁止这样的参数,你可以添加一个重载的移动构造函数:
SomeClass(const SomeClass&&) = delete;
注意:你正在尝试禁止这些论点,而不是禁止移动行为。因为我们通常无法从const
对象“窃取”资源,即使它是rvalue,所以调用复制构造函数而不是移动构造函数是合理的。如果这是XY problem,你应该考虑它是否真的要禁止这些论点。
答案 2 :(得分:1)
是否有可能使编译器发出警告/错误,std :: move将无法生效,因为无法移动对象?
std::move
不会产生任何影响,这是不正确的。以下代码(在wandbox上试用):
void foo(const SomeClass&) {
std::cout << "calling foo(const SomeClass&)" << std::endl;
}
void foo(const SomeClass&&) {
std::cout << "calling foo(const SomeClass&&)" << std::endl;
}
int main() {
foo(getObject());
foo(std::move(getObject()));
}
将输出
calling foo(const SomeClass&)
calling foo(const SomeClass&&)
即使你的对象有一个删除的移动构造函数。
原因是std::move
本身并没有“移动”任何东西。它只是一个简单的演员(C ++ 17 N4659选秀,23.2.5前锋/移动助手):
template <class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept; Returns: static_cast<remove_reference_t<T>&&>(t)
这就是为什么编译不会发出警告 - 一切都是完全合法的,你正在做的演员与删除的移动构造函数无关,而重载决策选择复制构造函数作为最佳匹配。
当然,如果你真的需要这样的语义(比如matthewscottgordon的答案中的那个),你可以使用与move
不同的语义来定义自己的std::move
。