簡體   English   中英

什么是C ++中的復制/移動構造函數選擇規則? 什么時候發生移動復制后備?

[英]What is copy/move constructor choosing rule in C++? When does move-to-copy fallback happen?

第一個例子:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const A&) = delete;
    A(A&&) = default;
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

它完美地運作。 所以這里使用了MOVE構造函數。

讓我們刪除移動構造函數並添加一個副本:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const A&a) 
        : ref( a.ref.get() ? new int(*a.ref) : nullptr )
    {  }
    A(A&&) = delete;
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

現在編譯出現錯誤“ 使用已刪除的函數'A :: A(A &&)'
所以MOVE構造函數是必需的,並且沒有回退到COPY構造函數。

現在讓我們刪除copy-move和move-constructors:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

它與“ 使用已刪除的函數'A :: A(const A&)' ”編譯錯誤有關。 現在它需要一個COPY構造函數!
所以從移動構造函數到復制構造函數有一個回退(?)。

為什么? 有沒有人知道它是如何符合C ++標准的,以及在復制/移動構造函數中選擇的實際規則是什么?

在檢查是否delete之前,選擇它將要使用的功能。 在復制構造函數可用且移動構造函數為delete d的情況下,移動構造函數仍然是兩者中的最佳選擇。 然后它看到它是delete d並給你一個錯誤。

如果您有相同的示例但實際上刪除了移動構造函數,而不是將其delete d,您將看到它編譯正常並且回退使用復制構造函數:

#include <iostream>
#include <memory>
using namespace std;

struct A {
    unique_ptr<int> ref;
    A(const A&a) 
        : ref( a.ref.get() ? new int(*a.ref) : nullptr )
    {  }
    A(const int i) : ref(new int(i)) { }
    ~A() = default;
};

int main()
{
    A a[2] = { 0, 1 };
   return 0;
}

這個類根本沒有為它聲明的移動構造函數(甚至沒有隱式),因此無法選擇它。

沒有“后備”。 它被稱為重載分辨率 如果在重載決策中有多個可能的候選者,則根據一組復雜的規則選擇最佳匹配,您可以通過閱讀C ++標准或其草稿來找到這些規則。

這是一個沒有構造函數的例子。

class X { };

void func(X &&) { cout << "move\n"; }            // 1
void func(X const &)  { cout << "copy\n"; }      // 2

int main()
{
    func( X{} );
}
  • 原樣:打印“移動”
  • 注釋掉“1”:打印“復制”
  • 注釋掉“2”:打印“移動”
  • 注釋掉“1”和“2”:無法編譯

在重載決策中,綁定rvalue到rvalue的優先級高於左值的rvalue。


這是一個非常相似的例子:

void func(int) { cout << "int\n"; }      // 1
void func(long) { cout << "long\n"; }    // 2

int main()
{
     func(1);
}
  • 原樣:打印“int”
  • 注釋掉“1”:打印“長”
  • 注釋掉“2”:打印“int”
  • 注釋掉“1”和“2”:無法編譯

在重載分辨率中,完全匹配優先於轉換。


在這個主題的三個例子中,我們有:

1:兩個候選功能; rvalue更喜歡rvalue(如我的第一個例子)

A(const A&);
A(A&&);           // chosen

2:兩個候選功能; rvalue更喜歡rvalue(如我的第一個例子)

A(const A&); 
A(A&&);           // chosen

3:一個候選功能; 沒有比賽

A(const A&);      // implicitly declared, chosen

如前所述 ,在案例3中沒有隱含的A(A &&)聲明,因為你有一個析構函數。

對於重載解析,函數體是否存在無關緊要,它是否聲明了函數(顯式或隱式)。

暫無
暫無

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

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