[英]Copy ctor is called instead of move ctor - Can compiler give a warning?
在下面的代碼中,我想移動構造一個沒有可用的移動構造器的對象:
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());
如果您只是確定要獲得最佳性能,那么在大多數情況下,您應該只信任編譯器。 實際上,您幾乎根本不需要使用std::move()
。 例如,在上面的示例中,它沒有效果。 現代編譯器可以確定何時應該發生移動。
如果您有應該始終移動且永遠不要復制的類,請刪除其復制構造函數。
但是也許您正在編寫一個模板函數,如果您將其傳遞給沒有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的原因。 在此處查看有關生成警告的更多討論。
這個問題源於const
右值。 這樣的參數匹配const T&
優於T&&
。 如果您確實想禁止此類參數,則可以添加重載的move構造函數:
SomeClass(const SomeClass&&) = delete;
注意:您要嘗試的是禁止此類參數,而不是禁止移動行為。 因為我們通常不能從const
對象中“竊取”資源,即使它是一個右值,所以調用復制構造函數而不是move構造函數也是合理的。 如果這是XY問題 ,則應考慮是否真的打算禁止此類參數。
是否有可能使編譯器發出警告/錯誤,提示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&&)
即使您的對象具有刪除的move構造函數。 原因是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)
這就是為什么編譯器不會發出警告-一切都完全合法,正在執行的強制轉換與已刪除的move構造函數無關,並且重載分辨率將copy構造函數選擇為最佳匹配。
當然,如果您確實需要這樣的語義(例如,matthewscottgordon的答案中的語義),則可以使用不同於std::move
語義來定義自己的move
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.