这方面的机制很好地解释了: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:
const Person p("asd");
Person p4(p); // thats ok, generator constructor is a better match
但如果我明确删除了一个带有:
的移动构造函数Person(Person&&) = delete;
然后禁止自动生成构造函数。
答案 0 :(得分:5)
你理解错了。
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
生成了移动/复制构造/赋值。
程序员编写的复制/移动构造/赋值会导致其他构造/赋值被抑制。
现在,编写构造函数会抑制零参数构造函数。这可能就是你困惑的原因。
与复制构造函数具有相同签名的模板化构造函数不是复制构造函数,因为标准是这样的。
实际上,模板化代码很少是复制或移动构造函数/赋值的正确代码。
转发引用经常抓取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
涵盖了其他一些情况(比如继承构造函数)。