2017-03-01 113 views
2

這裏的機制很好地解釋了:Template "copy constructor" does not prevent compiler-generated move constructor,但我想更好地理解爲什麼這樣做。我明白即使任何其他構造函數是由程序員編寫的,也不會生成移動構造函數,因爲它表明構造對象不是微不足道的,自動生成的構造函數可能是錯誤的。那麼爲什麼具有與複製構造函數相同簽名的模板化構造函數不是簡單的命名複製構造函數?爲什麼編譯器在存在模板構造函數時會生成複製/移動構造函數?

例子:

class Person { 
public: 
    template<typename T> 
    Person(T&& t) : s(std::forward<T>(t)) { 
     std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 

    Person(int n) { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
    } 

    // No need to declare copy/move constructors as compiler will do this implicitly 
    // Templated constructor does not inhibit it. 
    //Person(const Person&) = default; 
    //Person(Person&&) = default; 

private: 
    std::string s; 
}; 

然後:

Person p("asd");  // OK! 
//Person p4(p);   // error as Person(T&&) is a better match 

,如果我做p常量:

const Person p("asd"); 
Person p4(p);   // thats ok, generator constructor is a better match 

,但如果我直接刪除,甚至有轉移構造函數:

Person(Person&&) = delete; 

然後禁止自動生成構造函數。

+0

「*我明白,即使任何其他構造函數是由程序員編寫的,也不會生成移動構造函數*」這不是事實。只有存在複製構造函數/賦值才能防止生成移動構造函數。 –

+0

@NicolBolas謝謝,我在Effective Modern C++中查找了第17項,並且還移動了構造函數/賦值運算符和析構函數,以防止生成移動構造函數或賦值運算符。 – mike

回答

4

你明白錯了。

struct noisy { 
    noisy() { std::cout << "ctor()\n"; } 
    noisy(noisy&&) { std::cout << "ctor(&&)\n"; } 
    noisy(noisy const&) { std::cout << "ctor(const&)\n"; } 
    noisy& operator=(noisy&&) { std::cout << "asgn(&&)\n"; return *this; } 
    noisy& operator=(noisy const&) { std::cout << "asgn(const&)\n"; return *this; } 
}; 

struct test { 
    noisy n; 
    test(int x) { (void)x; } 
}; 

test已產生移動/複製構建體/分配。

Live example

由程序員編寫的複製/移動構造/作業會導致其他人被壓制。

現在,編寫一個構造函數抑制零參數構造函數。這可能是你困惑的原因。


與複製構造函數具有相同簽名的模板化構造函數不是複製構造函數,因爲標準是這樣說的。

碰巧,模板代碼很少是複製或移動構造函數/賦值的正確代碼。

事實上,轉發引用經常抓取self&self const&&複製/移動實際的複製/移動操作是一個問題。 C++並不完美。

一般避免這個的方法是:

template<class T, 
    class=std::enable_if_t< 
    !std::is_same<std::decay_t<T>, Person>::value 
    > 
> 
Person(T&& t) : s(std::forward<T>(t)) { 
    std::cout << __PRETTY_FUNCTION__ << "\n"; 
} 

!std::is_base_of<Person, std::decay_t<T>>::value覆蓋一些其他情況下(例如繼承構造函數)。

+0

謝謝,我正在閱讀Effective Modern C++中的第27項,其中討論如何避免使用通用(或正在轉發)引用構造函數的問題。太糟糕了斯科特邁耶斯不會深入到SFINAE。 – mike

相關問題