簡體   English   中英

C ++為什么將左值傳遞給移動構造函數適用於模板?

[英]C++ why does passing an lvalue to a move constructor work for templates?

我有這個代碼,它不編譯,這是預期的。

這是錯誤: an rvalue reference cannot be bound to an lvalue

class SomeData
{
public:
    vector<int> data;

    SomeData()
    {
        cout << "SomeData ctor" << endl;
        data.push_back(1);
        data.push_back(2);
        data.push_back(3);
    }

    SomeData(const SomeData &other)
    {
        cout << "SomeData copy ctor" << endl;
        data = other.data;
    }

    SomeData(SomeData &&other)
    {
        cout << "SomeData move ctor" << endl;
        data = move(other.data);
    }

    ~SomeData()
    {
        cout << "SomeData dtor" << endl;
    }

    void Print() const
    {
        for(int i : data)
            cout << i;

        cout << endl;
    }
};

void Function(SomeData &&someData)
{
    SomeData localData(someData);
    localData.Print();
}

int main(int argc, char *argv[])
{
    SomeData data;
    Function(data);                       // ERROR

    data.Print();

    return 0;
}

但是,當我將Function()轉換為模板時,它工作正常,並使用SomeData的復制構造函數。

template<class T>
void Function(T &&someData)
{
    T localData(someData);                  // no more error
    localData.Print();
}


這是標准的C ++行為嗎?

我注意到視覺工作室在模板方面往往更寬容,所以我想知道我是否可以從所有兼容的C ++ 11編譯器中獲得相同的行為。

是。 在模板函數的情況下,編譯器推導出模板參數T ,使其與給定的參數匹配。

由於someData實際上是一個左值,因此T被推導為SomeData & 然后,在類型推斷之后, Function的聲明變為

void Function(SomeData & &&)

SomeData & && ,遵循參考折疊規則 ,成為SomeData &

因此,函數參數someData變為左值引用,並且這樣傳遞給localData的初始化。 注意(正如@aschepler正確指出的那樣) localData被聲明為T ,所以它本身就是SomeData &類型的引用。 因此,這里沒有復制構造 - 只是初始化引用。


如果您希望localData是實際副本,則必須將類型從SomeData &轉換為SomeData ,即您必須從類型中刪除& 你可以使用std::remove_reference來做到這std::remove_reference

template<class T>
void Function(T &&someData)
{
   /* Create a copy, rather than initialize a reference: */
    typename std::remove_reference<T>::type localData(someData);
    localData.Print();
}

(為此,你必須#include <type_traits> 。)

這確實是預期的行為。 表單的模板函數:

template< class T >
Ret Function( T&& param )
{
  ...
}

遵循特殊規則(Ret可以是或不是模板,沒關系)。 T &&被稱為通用引用,它基本上可以綁定到任何東西。 這是因為當模板推導開始並且param采用該形式時(請注意, vector<T>&&不是通用引用,對於任何其他模板參數也不是C<T> ),應用引用折疊規則:

T = int&=> T && = int &&&並且折疊為單個int&

完整的表格是:

& + && = &
&& + & = &
&& + && = &&

所以當你有上述功能時

int a = 5;
int b& = a;
Function( a ); // (1)
Function( b ); // (2)
Function( 3 ); // (3)

在情況1中,T = int&且推導出的類型是int&(因為a是左值)因此Function具有以下簽名:

Ret Function( int& param ) // int& && collapses to int&

在案例2中,T = int&

Ret Function( int& param ) // int& && collapses to int&

在案例3中,T = int

Ret Function( int&& param ) 

這種崩潰規則是委員會認為合理的完美轉發工作。 你可以在Scott Meyers的視頻中找到長篇故事

只是為了讓你放心:這不是MSVC問題,這實際上是預期的行為。

在模板中, &&在應用於模板類型參數時具有不同的含義。 它被稱為通用參考

我可以解釋一下,但是這篇文章很好地解釋了它,所以你應該閱讀它。

為了總結(並且非常不精確),通用引用能夠在需要時“衰減”成普通引用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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