拥有以下代码,为什么第一项作业不会在operator=
中调用模板Foo
,但第二项呢?这里有什么?即使用户定义的模板存在,是否有编译器生成的第一个赋值?
#include <iostream>
using namespace std;
struct UberFoo { };
struct Foo : public UberFoo
{
template<typename T> void operator=(const T& v) { cout << "const T&" << endl; Set(v); }
template<typename T> void operator=(T& v) { cout << "T&" << endl; return Set(v); }
virtual void Set(const Foo&) { cout << "Foo::Set(const Foo&)" << endl; }
virtual void Set(const UberFoo&) { cout << "Foo::Set(const UberFoo&)" << endl; }
};
struct Bar : public Foo
{
virtual void Set(const Foo&) { cout << "Bar::Set(const Foo&)" << endl; }
virtual void Set(const UberFoo&) { cout << "Bar::Set(const UberFoo&)" << endl; }
};
int main()
{
Bar a, b;
Foo & pa = a;
const Foo& rb = b;
const UberFoo & urb = b;
cout << "First" << endl;
pa = rb;
cout << endl << "Second" << endl;
pa = urb;
return 0;
}
答案 0 :(得分:5)
编译器仍在生成第一个赋值所绑定的非模板化 operator=
。在第二个赋值中,模板operator=
是一个更好的候选者(因为它不涉及强制转换),因此选择一个。
您可以在代码中添加以下内容:
Foo& operator=(const Foo&) = delete;
或强制进行正确的模板调用:
pa.operator=<Foo>(b);
The standard说(强调我的):
12.8复制和移动类对象,§12.8/ 17,第271页:
用户声明的复制赋值运算符X :: operator =是X类的非静态非模板成员函数,其中只有一个类型为X,X&amp;,const X&amp;的参数,挥发性X&amp;或const volatile X&amp;
§12.8/ 18,同页:
如果类定义没有显式声明一个复制赋值运算符,则会声明隐式。
答案 1 :(得分:1)
为什么第一个作业在Foo中没有调用模板operator =,但第二个呢?这里有什么?
除了威廉关于隐式生成的复制赋值运算符函数的答案所解释的事实之外,重载决策在这方面也有一定的作用。下面是模板参数替换后第一个赋值运算符的候选者,如编译器所示:
Foo& operator=(const Foo&); // implicitly generated
void operator=(const Foo&); // template generated
在所有条件相同的情况下,非模板函数优先于函数模板。根据C ++ 11(草案N3337),13.3.3 / 1(强调我的)
鉴于这些定义,一个可行的函数F1被定义为比另一个可行函数F2更好的函数,如果对于所有参数i,ICSi(F1)不是比ICSi更差的转换序列(F2 ),然后
- 对于某些参数j,ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是,
- 上下文是用户定义转换的初始化(见8.5,13.3.1.5和13.3.1.6)和 从返回类型F1到目标类型的标准转换序列(即,类型的 正在初始化的实体)是比标准转换序列更好的转换序列 F2的返回类型到目标类型。 [...]或者,如果不是那样,
- F1是非模板功能,F2是功能模板专业化,或者,如果不是,
- F1和F2是功能模板专精,F1的功能模板更专业 根据14.5.6.2中描述的偏序规则,比F2的模板。
这解释了为什么选择非模板重载。您可以通过一个小练习验证相同的内容:
void print(int, int) { std::cout << "int"; }
template <typename T> void print(T, T) { std::cout << "T"; }
print(1, 2); // prints int
print<>(1, 2); // prints T
至于第二项任务,它会看到
Foo& operator=(const Foo&); // implicitly generated
void operator=(const UberFoo&); // template generated
这里模板生成的函数比隐式生成的函数更接近匹配,因此它被选中。