为什么删除模板复制构造函数会导致赋值运算符功能失调?

Why delete of templete copy constructor cause assignment operator disfunctional?

我有如下代码,看起来有点混乱。我定义了一个模板 class。它有一个用户定义的构造函数。当我通过“operator =”声明此模板 class 的两个对象时,令我惊讶的是它的用户定义构造函数被调用。另外,删除了它的拷贝构造函数后,在“operator =”的解析过程中连编译都无法通过。模板构造函数与非模板构造函数有不同的规则吗class?

#include "iostream"
using namespace std;

template <typename T>
class MyPtr
{
private:
    T p_;
public:
    MyPtr(T P = NULL): p_(P)
    {
        cout<<"track!!"<<endl;
    }

    //MyPtr(const MyPtr<T> & P) = delete;
    ~MyPtr()
    {
 
    }
};

int main()
{
    int i=3;
    int j=4;
    MyPtr<int> p = i;
    MyPtr<int> pp = j;
}

Does templete constructors have different rules than non-templete class?

没有

MyPtr<int> p = i;(和 MyPtr<int> pp = j;)是 copy initialization。请注意,这是 initialization 但不是 assignment,因为效果 p 由构造函数 MyPtr::MyPtr(T) 初始化。例如

MyPtr<int> p = i; // initialization; constructors get called
p = j;            // assignment; assignment operators (operator=) get called

在C++17之前,i会先通过MyPtr::MyPtr(T)转换为MyPtr<int>,然后转换结果,即临时的MyPtr<int>为copied/moved 初始化 p;即使允许优化 copy/move 操作,也需要 copy/move 构造函数可访问。将复制构造函数声明为 delete 会使复制构造函数不可用并且不会生成移动构造函数,因此在 C++17 之前 MyPtr<int> p = i; 格式错误。

自 C++17 以来,优化是强制性的,并且不需要再次访问 copy/move 构造函数,这意味着您的代码可以在 C++17 模式下正常编译,即使将复制构造函数声明为 delete.

  • If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution. The result of the conversion, which is a prvalue temporary (until C++17) prvalue expression (since C++17) if a converting constructor was used, is then used to direct-initialize the object. The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used. (until C++17)