在标题中用作函数参数的"原始"类型前面删除"const"是否更好?

cha*_*com 48 c++ const header primitive-types

在代码审查过程中,我的一位同事向我提到,在标题中用作函数参数的"原始类型"前面的"const"是没有意义的,他建议删除这些"const".在这种情况下,他建议仅在源文件中使用"const".原始类型表示诸如"int","char","float"等类型.

以下是示例.

example.h文件

int ProcessScore(const int score);
Run Code Online (Sandbox Code Playgroud)

example.cc

int ProcessScore(const int score) {
  // Do some calculation using score
  return some_value;
}
Run Code Online (Sandbox Code Playgroud)

他的建议如下:

example.h文件

int ProcessScore(int score);  // const is removed here.
Run Code Online (Sandbox Code Playgroud)

example.cc

int ProcessScore(const int score) {
  // Do some calculation using score
  return some_value;
}
Run Code Online (Sandbox Code Playgroud)

但我有点困惑.通常,用户只会查看标题,因此如果标题和源文件之间存在不一致,则可能会导致混淆.

有人可以给出一些建议吗?

Sto*_*ica 70

对于所有类型(不仅仅是基元),函数声明中的顶级 const限定符将被忽略.所以以下四个都声明了相同的功能:

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);
Run Code Online (Sandbox Code Playgroud)

但是,在函数体内不会忽略const限定符.在那里它可以影响const的正确性.但这是该功能的实现细节.所以普遍的共识是这样的:

  1. 将const留在声明之外.它只是杂乱无章,并不会影响客户端调用该函数的方式.

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

  • @snb我认为这是一个非常重要的事情.如果我遇到一个`const` by-value参数,我会认为开发人员犯了错误或者缺乏C++经验.对于参数,`const`只有在它们是引用或指针的修饰符时才真正有意义,因为它们控制输入的可变性.对于通过值传递的类型,它没有提供真正的语义含义来向它添加"const".*编辑*:我应该添加,但是你选择接受参数,只需_be consistent_.一致性对可读性很重要; 否则`const`参数可能看起来有特殊含义. (5认同)
  • @snb - 根本不重要.除非你是必须具有声明和定义匹配的那种.所以,如果你到处都是`const`,那么客户就会使用你的API,然后你改变主意......构建系统会毫无理由地重建*所有*,除非你咬紧牙关并保持标题不变.所以不妨在声明中开始使用const. (4认同)
  • @Bitwize这是一个"非常重要"的论点.你对个人编码能力的假设与IMO无关.大多数好的ides也可以从你的源签名生成函数模板,其中const值限定实际上很重要,所以改变签名似乎非常烦人,因为没有任何好处.直到最近才有像Clion这样的东西检查过这个(就像过去一个月左右) (2认同)

Art*_*sky 41

在进行重载解析时,声明为const且没有const的函数参数是相同的.所以例如功能

void f(int);
void f(const int);
Run Code Online (Sandbox Code Playgroud)

是相同的,不能一起定义.因此,最好不要在声明参数中使用const来避免可能的重复.(我不是在谈论const引用或const指针 - 因为const修饰符不是顶层.)

这是标准的准确引用.

生成参数类型列表后,在形成函数类型时,将删除修改参数类型的任何顶级cv限定符.生成的已转换参数类型列表以及省略号或函数参数包的存在与否是函数的parameter-type-list.[注意:此转换不会影响参数的类型.例如,int(*)(const int p, decltype(p)*)并且int(*)(int, const int*)是相同的类型. - 结束说明]

const在函数定义中的用处是值得商榷的 - 其背后的推理与使用const声明局部变量相同 - 它向其他程序员演示了读取代码时该函数内部不会修改此值.

  • 似乎"For**all**types参数声明为const而没有const在进行重载解析时是相同的." 事实上,就语言而言,它们具有相同的*签名*; 不仅重载决策不能区分它:你甚至不能只有两个函数只在同一程序中定义的顶级参数constness不同.原因肯定是C/C++中的所有参数(包括指针)都是按值传递的,因此没有任何函数可以改变它们.重要的是要理解`const char*`限定*target*,而不是指针. (5认同)

Dút*_*has 15

遵循代码审查中给出的建议.

使用const的值参数没有任何语义的价值-它是唯一有意义的(可能)实施的功能-即使在这种情况下,我认为这是不必要的.

编辑:为了清楚:你的函数的原型是你的函数的公共接口.什么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
Run Code Online (Sandbox Code Playgroud)

使用const类似于TMI - 除了'x'被修改之外,它在函数内部的任何地方都是不重要的.

edit2:我也非常喜欢StoryTeller答案中的信息


Hum*_*ler 7

正如许多其他人已经回答的那样,从API的角度来看,以下都是等效的,并且对于重载决策是相同的:

void foo( int );
void foo( const int );
Run Code Online (Sandbox Code Playgroud)

但更好的问题是,这是否为此API的使用者提供了任何语义含义,或者它是否为实现的开发者提供了任何良好行为的执行.

如果没有明确定义的明确定义的开发人员编码指南,const标量参数就没有明显的语义含义.

来自消费者: const int不会改变您的意见.它仍然可以是文字,也可以来自另一个变量(无论const是非const)

来自开发人员: const int对变量的本地副本(在本例中为函数参数)施加限制.这只是意味着修改参数,你获取变量的另一个副本并改为修改它.

当调用接受参数by-value的函数时,在被调用函数的堆栈上复制该参数.这为函数提供了整个范围的参数的本地副本,然后可以对其进行修改,用于计算等,而不会影响传递给调用的原始输入.实际上,这提供了其输入的局部变量参数.

通过将参数标记为const,它只是意味着不能修改此副本; 但它并没有从复制它,并进行修改这个副本禁止开发.由于这是从一开始就是一个副本,它并没有从实现中强制执行 - 并且最终从消费者的角度来看并没有太大的区别.

这与通过引用传递相反,其中引用在int&语义上不同于const int&.前者能够改变其输入; 后者仅能够观测输入的(提供的实现并不const_castconst-ness -但可以忽略这种可能性); 因此,const参考文献的含义具有隐含的语义含义.

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

void do_n_times( int n )
{
   while( n-- > 0 ) {
       // do something n times
   } 
}
Run Code Online (Sandbox Code Playgroud)

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

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

无论是否const在公共API中使用标量,一个关键是与设计保持一致.如果API在使用const标量参数和使用非const标量之间随机切换,那么它可能会导致混淆是否意味着对消费者有任何暗示的含义.

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