在头文件中用作函数参数的“基元”types前面去除“const”是否更好?

在代码审查过程中,我的一位同事向我提到,在头部用作函数参数的“原始types”之前的“const”是毫无意义的,他build议删除这些“const”。 在这种情况下,他build议只在源文件中使用“const”。 原始types是指诸如“int”,“char”,“float”等的types

以下是例子。

example.h文件

int ProcessScore(const int score); 

example.cc

 int ProcessScore(const int score) { // Do some calculation using score return some_value; } 

他的build议是这样做的:

example.h文件

 int ProcessScore(int score); // const is removed here. 

example.cc

 int ProcessScore(const int score) { // Do some calculation using score return some_value; } 

但是我有些困惑。 通常情况下,用户只看标题,所以如果标题和源文件不一致,可能会造成混淆。

任何人都可以提供一些build议吗?

声明为const且不带const的函数参数在重载parsing时相同。 所以例如function

 void f(int); void f(const int); 

是一样的,不能一起定义。 因此最好不要在声明参数时使用const来避免可能的重复。 (我不是在谈论const引用或const指针 – 因为const修饰符不是顶级的。)

这里是准确的报价。

生成参数types列表后,修改参数types的任何顶级cv-qualifiers将在形成函数types时被删除。 变换的参数types的结果列表以及省略号或函数参数包的存在或不存在是函数的参数types列表。 [注意:这个转换不会影响参数的types。 例如, int(*)(const int p, decltype(p)*)int(*)(int, const int*)是相同的types。 – 结束注意]

const在函数定义中的用处是有争议的 – 推理的背后和使用const来声明局部variables是一样的 – 它向其他程序员演示读取代码,这个值不会在函数内被修改。

对于所有types(不只是基元),函数声明中的顶级 const限定符将被忽略。 所以下面的四个都声明了相同的function:

 void foo(int const i, int const j); void foo(int i, int const j); void foo(int const i, int j); void foo(int i, int j); 

然而,const限定符在函数体内不被忽略。 它可以影响常量的正确性。 但是这是该函数的一个实现细节。 所以普遍的共识是这样的:

  1. 把const放在声明之外。 这只是混乱,并不影响客户如何调用该function。

  2. 如果希望编译器捕捉参数的任何意外修改,请将const保留在定义中

遵循代码审查中给出的build议。

使用const作为值参数没有语义价值 – 它只是有意义的(可能)实现你的function – 即使在这种情况下,我会认为这是没有必要的。

编辑:只是要清楚:你的函数的原型是你的函数的公共接口const所做的是保证你不会修改引用。

 int a = 7; do_something( a ); void do_something( int& x ); // 'a' may be modified void do_something( const int& x ); // I will not modify 'a' void do_something( int x ); // no one cares what happens to x 

使用const类似于TMI,除了在函数内部修改'x'以外,其他部分都是不重要的。

编辑2:我也非常喜欢StoryTeller的答案中的信息

正如许多其他人所回答的,从API的angular度来看,以下所有方面都是等同的,并且在超载解决方面是相同的:

 void foo( int ); void foo( const int ); 

但是更好的问题是,这是否会为此API的使用者提供任何语义含义 ,或者是否提供了执行开发者的任何良好行为的强制执行。

如果没有任何明确定义的开发者编码准则,那么const标量参数就没有明显的语义含义

从一个消费者: const int不会改变你的input。 它仍然可以是一个文字,也可以来自另一个variables( const或非const

从开发者: const int对variables的本地副本 (在本例中是函数参数)施加限制。 这只是意味着修改参数,您需要另一个variables的副本,并修改它。

当调用一个接受by-value参数的函数时,在被调用的函数的栈上拷贝该参数。 这给函数的整个范围参数的本地副本,然后可以修改,用于计算等 – 而不影响传递到调用的原始input。 实际上,这提供了一个input的局部variables参数。

通过将参数标记为const ,仅仅意味着该副本不能被修改; 但是并不禁止开发者拷贝和修改这个拷贝。 由于这是从一开始就是一个副本,所以在执行过程中并没有太多的实施 – 最终与消费者的观点没有太大的区别。

这与通过引用传递相反,其中对int&的引用在语义上与const int&不同。 前者有能力改变投入; 后者只能观察input(只要实现不会const_cast消除const – 但忽略这种可能性); 因此, 参考文献中的const具有隐含的语义含义。

在公共API中没有提供太多好处; (imo)在实施中引入了不必要的限制。 作为一个任意的,人为的例子 – 一个简单的函数,如:

 void do_n_times( int n ) { while( n-- > 0 ) { // do something n times } } 

现在必须使用不必要的副本来编写:

 void do_n_times( const int n ) { auto n_copy = n; while( n_copy-- > 0 ) { // do something n times } } 

无论在公共API中是否使用const标量,关键是要与devise保持一致 。 如果API在使用const标量参数到使用非const标量之间随机切换,那么它可能会引起混淆,是否意味着对消费者有任何隐含的含义。

TL; DR:公共API中的const标量types不会传达语义,除非您自己的域指南明确定义。

认为 const是编译器提示一些expression式不会改变并相应地进行优化的提示。 例如,我正在testing一个数是否为素数,通过查找数字的平方根达到除数,我认为声明const的参数会将sqrt(n)放在for循环之外,但是不是。

在标题中可能没有必要,但是可以再说一次,你所需要的就是不要修改这个参数,这是不必要的。 我宁愿看const是const的地方,不仅在源代码中,而且在头文件中。 宣言和定义之间的不一致让我很小心。 只是我的观点。