簡體   English   中英

構造函數中的C ++通用引用和返回值優化(rvo)

[英]C++ universal reference in constructor and return value optimization (rvo)

為什么在帶有通用引用參數的構造函數的類中不會出現右值優化?

http://coliru.stacked-crooked.com/a/672f10c129fe29a0

#include <iostream>

 template<class ...ArgsIn>
struct C {

  template<class ...Args>
  C(Args&& ... args) {std::cout << "Ctr\n";}        // rvo occurs without &&

  ~C(){std::cout << "Dstr\n";}
};

template<class ...Args> 
auto f(Args ... args) {
    int i = 1;
  return C<>(i, i, i);
}

int main() {
  auto obj = f();
}

輸出:

Ctr
Ctr
Dstr
Ctr
Dstr
Dstr

我認為問題在於實例化

template<class ...Args>
C(Args&& ... args) {std::cout << "Ctr\n";}  

就語言而言,不是復制/移動構造函數,因此編譯器不能忽略對它們的調用。 從§12.8[class.copy] / p2-3開始,增加了重點並省略了示例:

如果X類的第一個參數是X&const X&volatile X&const volatile X& ,並且沒有其他參數或者所有其他參數都有默認參數,則類X 非模板構造函數是一個復制構造函數(8.3.6) )。

如果X類的第一個參數的類型為X&&const X&&volatile X&&const volatile X&& ,並且沒有其他參數,或者所有其他參數都有默認參數(8.3),則const X&&類的非模板構造函數是一個移動構造const X&& 。 6)。

換句話說,作為模板的構造函數永遠不能是復制或移動構造函數。

返回值優化是copy elision的一個特例,描述為(§12.8[class.copy] / p31):

當滿足某些條件時,允許實現省略類對象的復制/移動構造,即使為復制/移動操作選擇的構造函數和/或對象的析構函數具有副作用。

這允許實現省略“復制/移動構造”; 使用既不是復制構造函數也不是移動構造函數的東西構造對象不是“復制/移動構造”。

因為C具有用戶定義的析構函數,所以不會生成隱式移動構造函數。 因此,重載決策將選擇模板化的構造函數,其中Args推導為C ,這比rvalues的隱式復制構造函數更好。 但是,編譯器不能忽略對此構造函數的調用,因為它具有副作用,既不是復制構造函數也不是移動構造函數。

如果是模板化的構造函數

template<class ...Args>
C(Args ... args) {std::cout << "Ctr\n";} 

然后它不能用Args = C實例化以生成復制構造函數,因為這將導致無限遞歸。 標准中有一條特殊規則禁止此類構造函數和實例化(§12.8[class.copy] / p6):

如果類X的構造函數的第一個參數是類型(可選地是cv-qualified) X並且沒有其他參數或者所有其他參數都具有默認參數,那么它的構造函數的聲明是錯誤的。 從不實例化成員函數模板以生成此類構造函數簽名。

因此,在這種情況下,唯一可行的構造函數將是隱式定義的復制構造函數,並且可以省略對該構造函數的調用。

如果我們改為從C 刪除自定義析構函數,並添加另一個類來跟蹤何時調用C的析構函數:

struct D {
    ~D() { std::cout << "D's Dstr\n"; }
};

template<class ...ArgsIn>
struct C {
  template<class ...Args>
  C(Args&& ... args) {std::cout << "Ctr\n";}
  D d;
};

我們只看到對D的析構函數的一次調用,表明只構造了一個C對象。 這里C的移動構造函數是通過重載解析隱式生成和選擇的,並且您再次看到RVO啟動。

暫無
暫無

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

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