簡體   English   中英

調用復制ctor而不是移動ctor-編譯器可以發出警告嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM