在header中用作函数参数的"primitive"类型前面的"const"是不是更好?

Is it better to remove "const" in front of "primitive" types used as function parameters in the header?

在代码审查过程中,我的一位同事向我提到,"primitive types"前面的"const"s用作header中的函数参数是没有意义的,他建议删除这些 "const"。他建议在这种情况下仅在源文件中使用 "const"。基本类型是指"int"、"char"、"float"等类型

示例如下

example.h

int ProcessScore(const int score);

example.cc

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

他的建议是这样的:

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;
}

但我有点困惑。通常,用户只会查看 header,因此如果 header 与源文件不一致,可能会造成混淆。

谁能对此提出一些建议?

声明为 const 和不声明为 const 的函数参数在进行重载解析时是相同的。例如函数

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

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

这里是标准的准确引用。

After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function’s parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. — end note ]

函数定义中 const 的有用性值得商榷 - 它背后的推理与 使用 const 声明局部变量相同 - 它向阅读代码的其他程序员展示了 this 值不会在函数内部修改。

遵循代码审查中给你的建议。

对值参数使用 const 没有语义价值——它仅(可能)对你的函数的实现有意义——即使在那种情况下,我认为它是不必要的。

编辑: 需要明确的是:您的函数原型是您函数的 public 接口 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' 是否被修改,除了函数内部之外的任何地方都不重要。

edit2:我也很喜欢

中的信息

对于所有 类型(不仅仅是基元),函数声明中的顶级 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);

然而,const 限定符在函数 body 中不会被忽略。在那里它可能会影响 const 的正确性。但这是函数的实现细节。所以普遍的共识是:

  1. 将 const 排除在声明之外。这只是混乱,不会影响客户调用函数的方式。

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

认为 const 是对编译器的提示,即某些表达式不会更改并相应地进行优化。例如,我通过寻找除数直到数字的平方根来测试一个数字是否为质数,我认为声明参数 const 会将 sqrt(n) 置于 for 之外循环,但它没有。

在 header 中可能没有必要,但是话又说回来,您可以说您所需要的只是不修改参数,而且永远没有必要。我宁愿看到 const 是 const 的地方,不仅在源代码中,而且在 header 中也是如此。声明和定义之间的不一致让我很谨慎。只是我的意见。

正如其他许多人所回答的,从 API 的角度来看,以下内容都是等价的,并且对于重载解析是等价的:

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

但一个更好的问题是,这是否为这个 API 的消费者提供了 任何语义意义,或者它是否提供了对良好行为的强制执行实施的开发者

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

来自消费者: const int 不会更改您的输入。它仍然可以是文字,也可以来自另一个变量(const或非const

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

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

通过将参数标记为const,它只是意味着这个副本不能被修改;但它并不禁止开发人员复制它并对此副本进行修改。由于这是从一开始就复制的,因此它并没有从实施内部强制实施那么多——最终从消费者的角度来看也没有太大区别。

这与 通过引用传递 形成对比,其中对 int& 的引用在语义上不同于 const int&。前者能够改变其输入;后者只能观察输入(前提是实现不会 const_cast const-ness 消失——但让我们忽略这种可能性);因此,引用的 const-ness 具有隐含的语义。

在 public 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
    }
}

无论 public API 中是否使用 const 标量,关键是要与设计 一致 。如果 API 在使用 const 标量参数和使用非 const 标量参数之间随机切换,那么它可能会导致消费者混淆是否对消费者有任何隐含意义。

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