函数声明中const关键字的位置

9

可能是重复问题:
C++中const声明的区别

#include <iostream>

class Bar{};

void foo(const Bar x){}  //l5
void foo(Bar x){}        //l6
void foo(Bar const x){}  //l7

////pointer functions

void foo(const Bar* x){} //l11
void foo(Bar* x){}       //l12
void foo(Bar* const x){} //l13

编译器输出:(长话短说,l5l6l7 冲突;但只有 l12l13 冲突)

untitled.cpp:6:6: error: redefinition of ‘void foo(Bar)’
untitled.cpp:5:6: error: ‘void foo(Bar)’ previously defined here
untitled.cpp:7:6: error: redefinition of ‘void foo(Bar)’
untitled.cpp:5:6: error: ‘void foo(Bar)’ previously defined here
untitled.cpp:13:6: error: redefinition of ‘void foo(Bar*)’
untitled.cpp:12:6: error: ‘void foo(Bar*)’ previously defined here

发生了什么事?

  1. 每个声明的含义是什么?
  2. 为什么所有3个声明都与对象函数冲突,但只有2个与指针函数冲突?
  3. 请详细说明l12l13之间的冲突,即使l12不包含const关键字
  4. 如果问题微不足道,请多多包涵。
6个回答

6
"问题"在于参数值的const特性不参与重载!
首先,Bar constconst Bar已经具有相同的含义,因此它们自动会遇到问题。但是,在函数参数中,const不适用于重载,因此函数的Bar版本看起来也是相同的。参数中的const仅告诉编译器您不打算在函数体中修改它。
出于同样的原因,Bar*Bar* const被视为相同: const适用于参数的值(而不是指向的内容),并且不参与重载,因此您定义了相同的函数。
另一方面,const Bar*意味着完全不同的意思:指向类型为Barconst对象的非const指针。由于类型不同,它参与重载并允许该函数成为唯一的。

2

在类型名称前或后加上const没有任何区别。

15和17具有相同的参数列表。
这两个函数被认为具有相同的原型并且是重复的。

函数#1

void foo(const int x) {
return;
}

函数 #2 - 重复参数列表

void foo( int const x) {
return;
}

const的位置和你例子中的15和17是相同的。

根据维基百科,两种方式都可以使用:

http://en.wikipedia.org/wiki/Const-correctness


关于您的第一句话:在这种特定的上下文中,编译器并不关心。然而,通常情况下,“const”会修改它之前的内容,并且系统地将“const”放在所修改的内容之后(即“int const”,而不是“const int”)确实可以使代码更加可读。 - James Kanze

2
基本上,因为C++在调用函数时会复制值,所以从调用者的角度来看,前三个参数没有区别。(调用者知道函数不能更改它正在传递的参数的值,因此在很多方面,每个函数参数从调用者的角度来看都是隐含的常量。)
然而,当谈到指针时,如果您传递一个指向常量的指针与一个指向非常量的指针,对于调用者会有所不同(其中一个不会更改您的内容,另一个可能会)。这就是 l11 和 l12 不冲突的原因。 l12 和 l13 冲突,因为它们都是指向 Bar* 的指针(其中一个是 const 指针,另一个不是,因此和 l5-l7 一样,调用者没有区别)。
最后一个要点可能有点棘手 - 请注意,虽然 int const *a 与 const int *a 相同,但它们与 int * const a 不同,前两个是指向常量 int 的指针,而另一个是指向 int 的常量指针(即后者指针的值不能更改)。

我很感激你准确的解释。 :) - Smart Humanism

0

前三个函数产生冲突的原因是编译器无法确定在任何情况下使用哪个函数。当您调用 foo(BarObject); 时,编译器可能会使用任何一个函数,无论是否将 BarObject 声明为 const

但是,在参数为指针的函数中,当您调用 foo(BarPointer); 时,如果 BarPointer 被声明为 const Bar* BarPointer;,则编译器将选择 ]11,因为它确保在函数中不会修改所指向的对象(与传递值的前三种情况不同)。如果它不是 const,则不知道是否应该调用 ]12]13,因为 Bar* const x 的含义是“x 不能指向除作为参数传递的内容以外的任何东西”,这与调用者无关。

有关声明的简要参考:

const Bar x // x is an immutable copy of the original parameter.
Bar const x // same as above and gets compiled, but cdecl says it is a syntax error.

const Bar* x // x points to an object that can't be changed.
Bar* const x // x can't point to any other object than the parameter passed.

0
void foo(const Bar x){}
void foo(Bar const x){}

上述两者是相同的,因为它们都表明 xBar 类型,并且它是 const
void foo(Bar x){}

这个与前面两个冲突,因为x是否是const是您函数的一个实现细节,并且编译器会从函数签名中删除它。因此,所有3个函数最终具有相同的签名,即void foo( Bar x )

void foo(Bar* x){}
void foo(Bar* const x){}

这与前面的情况类似;您正在指示指针xconst,即您不会在函数内部重新指向x到其他东西。在两种情况下,x指向的Bar对象都是非const的。因此,xconst性质是函数的实现细节。

void foo(const Bar* x){}

在这里,您指示x指向一个constBar对象。这与前两种情况不同,因此没有冲突。


0
对于前三个函数 - 在变量通过值传递的情况下,const 对于重载分辨率并不重要。参数的副本在堆栈上创建,从外部(调用者)的角度来看,这个副本是否被更改并不重要。但对于函数本身(内部)而言则很重要。
对于第二种情况,基于指针的函数,它是函数重载分辨率的重要部分,因为不会在堆栈上创建副本,从外部(调用者)的角度来看,这意味着函数将或不会修改参数的值。
对于最后两个函数,告诉编译器:有一个指向Bar的指针x,并且我可能会更改由x指向的Bar值。但在第一种情况下,您可以更改指针x本身的值(例如,指向另一个Bar),与第二种情况相反。这里我们处于第一种情况 - 指针本身的副本在堆栈上,如果它们在函数内部更改了,那么对于重载分辨率来说是没有意义的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接