在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 的正确性。但这是函数的实现细节。所以普遍的共识是:
将 const 排除在声明之外。这只是混乱,不会影响客户调用函数的方式。
如果您希望编译器捕获参数的任何意外修改,请在定义中保留 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 中的标量类型不传达语义含义,除非您自己明确定义您的域的指南。
在代码审查过程中,我的一位同事向我提到,"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)*)
andint(*)(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 的正确性。但这是函数的实现细节。所以普遍的共识是:
将 const 排除在声明之外。这只是混乱,不会影响客户调用函数的方式。
如果您希望编译器捕获参数的任何意外修改,请在定义中保留 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 中的标量类型不传达语义含义,除非您自己明确定义您的域的指南。