使用其他相同签名重载函数以接受引用和const引用

时间:2014-12-10 10:01:10

标签: c++ reference const overloading

代码如下:

struct A {};   

void f (A& a) {std::cout << "No const" << std::endl;} 

void f (const A& a) {std::cout << "const" << std::endl;} 

int main(){
   f(A()); // indicated as line A, output will be "const".
}

问题1):

为什么行A输出“const”,即使A()是非const对象?

我认为编译器产生的代码相当于:

const A tempA;
f(tempA); 

是吗?

问题2),如果修改了两个f函数,则更改对值类型的引用

void f (A a) {std::cout << "No const" << std::endl;} 

void f (const A a) {std::cout << "const" << std::endl;}  

以上代码无法编译。编译器给出“函数f的重新定义”错误。我可以理解为什么错误是由编译器发出的,因为可以将const A转换为A,反之亦然。因此,无法在编译时决定。是吗?

我仍然非常好奇,它在c ++规范中定义得很好吗?

3 个答案:

答案 0 :(得分:5)

  

为什么A行的输出是“const”,即使A()是没有const的对象?

A()表达式创建一个不能被非const左值引用绑定的prvalue临时值,因此void f (const A& a)是唯一可行的重载。

  

我仍然非常好奇,它在c ++规范中有很好的定义吗?

§13.1[over.load] / p3:

  

- 仅在const和/或volatile存在或不存在时有所不同的参数声明是   当量。也就是说,当忽略每个参数类型的constvolatile类型说明符时   确定正在声明,定义或调用哪个函数。 [示例

typedef const int cInt;
int f (int);
int f (const int);           // redeclaration of f(int)
int f (int) { /* ... */ }    // definition of f(int)
int f (cInt) { /* ... */ }   // error: redefinition of f(int)
     

- 结束示例]

     

以这种方式只忽略参数类型规范最外层的constvolatile类型说明符;埋在参数类型规范中的constvolatile类型说明符很重要,可用于区分重载的函数声明。特别是,对于任何类型T,“指向T的指针”,“指向const T的指针”和“指向volatile T的指针”被视为不同的参数类型, “引用T”,“引用const T”和“引用volatile T。”

对于Aconst A类型的参数,当任何可转换为A的参数初始化时,相同的转换序列适用,编译器永远无法在两者之间进行选择。

对于A&const A&类型的参数,可以有不同的表达式,以便编译器可以选择更好的转换序列或从候选集中排除任何候选。

答案 1 :(得分:2)

在你的第一个案例中:

A()创建一个临时的,传递给你的函数。 Temporaries只能通过规范绑定到const左值引用。因此,选择了const重载。

请注意,Visual C ++扩展允许非const值左侧引用绑定到临时值。这是非标准的,默认情况下已启用,因此请注意。我不知道在那种情况下会选择什么超载。

在你的第二个案例中:

函数参数的const限定符限定参数本身,而不是作为其参数的对象。因此,您的两个函数之间的唯一区别是函数内部 参数的const
在第一个参数是指针或对可能的const对象的引用的情况下,转换和绑定规则使单个过载可用(或者更可取)用于以后的函数调用。
然而,在第二种情况下,没有任何限定你的参数,因此两个重载都无法通过设计区分(以后的调用都不能明确),因此“多个定义”错误。

答案 2 :(得分:0)

你正在创建一个临时对象来传递给函数f(A());构造的对象仅用于函数。临时工总是常数。如果您在函数调用之外构造对象,这将起作用。实施例

A nonConstA;
f(nonConstA); //Will print "No const"

const A constA;
f(constA); //Will print "const"