2014-06-18 84 views
6

有以下代码,为什么第一个赋值不会在Foo中调用模板operator=,但第二个呢?这里发生了什么?即使用户定义的模板存在,是否有编译器为第一个赋值生成的?使用模板赋值运算符

#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

我知道赋值运算符应该返回参考自我(做链接可能),但这不是重点 – relaxxx

+0

是的,一个拷贝赋值运算符始终定义,除非明确地删除。如果你没有定义一个,编译器会。 –

回答

5

编译器仍旧产生非模板operator=其中第一分配结合。在第二个作业中,模板operator=是一个更好的候选人(因为它不涉及演员表),所以选择一个。

你可以看到,通过添加代码如下:

Foo& operator=(const Foo&) = delete; 

或者强迫正确的模板调用:

pa.operator=<Foo>(b); 

The standard说(重点煤矿):

12.8复制和移动课程对象,§12.8/ 17,第271页:

甲用户声明的复制赋值运算符X ::运算符=是类X的非静态非模板成员函数正好与一个参数类型X的,X &,常量X &,易失性X &或const volatile的X &

§12.8/ 18,同一页:

如果类定义不明确声明拷贝赋值运算符,一个声明含蓄

+0

以及生成的签名是怎样的?当'T'推导为'Foo'签名是相同的:'void运算符=(const的富&)',所以这是不如编译器生成的一个。为什么'T'不能被推断为'Foo'? – relaxxx

+0

@relaxxx:它可能与复制赋值运算符“一样好”,但根据此答案中引用的规则,它不是复制赋值运算符。 'T'可以推断为'Foo'但没有必要的发生,因为已经有一个匹配的非模板函数调用,并且优先。 –

+0

@relaxxx正如LRIO所说的,签名是一样的,但功能是模板化的。在重载解析期间,非模板比模板匹配得更好,所以删除的操作符被选为最佳候选。 –

1

为什么第一次分配不调用模板运营商= Foo中,但第二个呢?这里发生了什么?

除了William对隐式生成的复制赋值运算符函数的解释所解释的事实之外,重载解析在这方面也有所发挥。以下是第一个赋值运算符的考生,模板参数替换后,由编译器所看到:

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)更好的转换序列,或者if不是,

- 上下文是通过用户定义的转换进行初始化(请参阅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 

这里生成的模板函数比隐式生成之一,因此它是一个更匹配选择。