2017-03-01 127 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

相关问题