对此的机制进行了很好的解释:模板“复制构造函数”不会阻止编译器生成的move构造函数,但我想更好地理解为什么这样做。我知道即使程序员编写了任何其他构造函数,也不会生成move构造函数,因为这表明对象的构造很简单,并且自动生成的构造函数可能是错误的。那么,为什么具有与副本构造函数相同签名的模板化构造函数不能简单地命名为副本构造函数呢?
例:
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;
};
Run Code Online (Sandbox Code Playgroud)
然后:
Person p("asd"); // OK!
//Person p4(p); // error as Person(T&&) is a better match
Run Code Online (Sandbox Code Playgroud)
如果我做pconst:
const Person p("asd");
Person p4(p); // thats ok, generator constructor is a better match
Run Code Online (Sandbox Code Playgroud)
但是如果我显式删除甚至带有以下内容的move构造函数:
Person(Person&&) = delete;
Run Code Online (Sandbox Code Playgroud)
然后禁止自动生成构造函数。
你理解错了。
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; }
};
Run Code Online (Sandbox Code Playgroud)
test 已产生移动/复制构造/分配。
现场例子。
程序员编写的复制/移动构造/赋值导致其他复制/移动构造/赋值被抑制。
现在,编写构造函数可消除零参数构造函数。这就是为什么你感到困惑。
具有与副本构造函数相同签名的模板化构造函数不是副本构造函数,因为标准如此规定。
碰巧的是,模板化代码很少是复制或移动构造函数/赋值的正确代码。
转发引用通常会在实际的复制/移动操作上进行抓取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";
}
Run Code Online (Sandbox Code Playgroud)
或!std::is_base_of<Person, std::decay_t<T>>::value涵盖其他一些情况(例如继承构造函数)。