为什么编译器在分配时会调用模板化的复制构造函数?

时间:2017-03-30 21:27:15

标签: c++ templates type-conversion copy-constructor implicit-conversion

请考虑以下代码:

b = a;

Live on Coliru

如您所见,赋值运算符已注释掉。但是,行

a

编译好。我认为它不应该编译,因为boperator=(const X&)具有不同的类型,并且编译器默认生成的X<int>将由底层类型实例化,因此它赢了&# 39;将X<double>分配给a

令我惊讶的是,代码编译,似乎正在调用模板化的复制构造函数。为什么是这样?是因为编译器首先尝试将b强制转换为B::operator=,然后调用默认生成的=SUMPRODUCT(B3:B25,--(AD3:AD25))

3 个答案:

答案 0 :(得分:3)

如果TU是不同的类型,template<class U> X(const X<U>&)不是复制构造函数,因为它的参数类型不同。换句话说,X<int>X<double>是不相关的类型,因此这个构造函数只是它们之间的用户定义转换。

请注意,此代码不会打印任何内容:

X<int> a;
X<int> b { a };

因为在这种情况下,将调用X<int>::X(const X<int>&)形式的隐式声明的复制构造函数。

答案 1 :(得分:1)

编译器正在生成对X<double>(const X<int>&)的调用,以将X<int>转换为X<double>。然后,它调用生成的赋值运算符X<double>& X<double>::operator =(const X<double>&);来进行赋值。

如果你明确写出了所有步骤,那就是:

b.operator =(X<double>(a));

答案 2 :(得分:1)

template<class U> X(const X<U>&)是一种广义的用户定义转换 - 请参阅Anton Savin的回答。

隐式类型转换的规则非常复杂,因此您应该警惕用户定义的转换函数(More Effective C++ Item 5)。模板函数更是如此。

代码

#include <iostream>

template<class T>
struct X
{
    X() = default;

    template<class U>
    X(const X<U>&)
    {
        std::cout << "generalized ctor: " << __PRETTY_FUNCTION__ << std::endl;
    }

/*
    template<class U>
    X& operator=(const X<U>&)
    {
        std::cout << "generalized assignment: " << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }
*/
};

int main()
{
    X<int> a;
    X<double> b;

    b = a;
}

返回

  

广义ctor:X :: X(const X&amp;)[U = int; T =双倍]

但是operator=没有注释掉,它会返回

  

广义任务:X&amp; X :: operator =(const X&amp;)[U = int; T =双倍。

因此,您是正确的,隐式生成的operator=(const X&)将由基础类型实例化,并且不会将X<int>分配给X<double>。正如Scott Meyers(参见上面的参考资料)所解释的那样,您的编译器面临对b.operator=的{​​{1}}的调用, X<double>,并发现不存在此类函数。因此,您的编译器会尝试查找可应用的隐式类型转换的可接受序列,以使调用成功,see rules in the cpp reference。您的编译器会找到您的通用用户定义转换,并将const X<double>转换为X<int>,使其具有X<double>的正确参数类型。