在C++中,定义但未声明的const值参数真的存在吗?

3

这与这个问题类似(但不同)。

以下是一些简单的测试代码,用于说明我在使用Sun CC时发现的一些奇怪问题:

//---------------main.cpp
#include "wtc.hpp"

int main(int, char**)
{
  testy t;
  t.lame(99);
  return 0;
}
//--------------wtc.hpp
#ifndef WTC_HPP_INCLUDED
#define WTC_HPP_INCLUDED

class testy
{
public:
  void lame(int );
};

#endif 

//---------------wtc.cpp
#include <iostream>
#include "wtc.hpp"

void testy::lame(const int a)
{
  std::cout << "I was passed " << a << "\n";
}

//---------------makefile
#CXX=CC
CXX =g++
#CXXFLAGS= -g 
CXXFLAGS= -g3 -Wall -Werror

OBJECTS=$(patsubst %.cpp,%.o,$(wildcard *.cpp))

all : $(OBJECTS)
    $(CXX) $(CXXFLAGS) -o $@ $^

.PHONY: clean
clean :
    rm *.o

当使用g++编译时,它会编译、链接并在运行时按预期执行。您还可以在testy::lame()中添加++a;,编译器将抱怨更改只读变量(应该如此)。

然而,当我使用CC编译时,我得到以下链接器错误:

CC -g   -c -o main.o main.cpp
CC -g   -c -o wtc.o wtc.cpp
CC -g -o all main.o wtc.o
Undefined                       first referenced
 symbol                             in file
void testy::lame(int)               main.o
ld: fatal: Symbol referencing errors. No output written to all
make: *** [all] Error 1

通过使用nm和C++filt检查目标代码,我发现g++版本创建了一个testy::lame(int)符号,而CC创建了testy::lame(const int),因此出现了链接错误。

我在Stroustrup的书中查找了这个技术,但没有找到提到它(这并不意味着它不存在!);所以这真的是一个编译器bug,还是一个在Solaris之外的任何地方都可以工作的hack?


你向Oracle发送了错误报告吗? - anxieux
7个回答

27

这看起来像是CC编译器的问题。根据C++标准中的13.1重载声明:

仅在const和/或volatile存在与否上有所不同的参数声明是等效的。也就是说,当确定正在声明、定义或调用哪个函数时,每个参数类型的const和volatile类型限定符都被忽略。

但是标准随后提到了可以参与重载的const/volatile修饰符:

仅位于参数类型规范的最外层的const和volatile类型限定符会以这种方式被忽略;嵌入参数类型规范中的const和volatile类型限定符是有意义的,可以用于区分重载的函数声明。


4
+1,对我来说属于“我理解这种逻辑,但它似乎只会让语言变得更奇怪”的类别。 - JaredPar
这在参数被复制的情况下是有意义的:一个 const int 可以被复制到一个 int 中,因为它们是分别存储的。然而,一个 const int* 不能被复制到一个 int* 中,因为 const 的东西是共享的。 - Dan
@JaredPar,这里的一个使用案例是更改API以确保您的实现可以修改值参数毫无意义。但是,您可能希望在实现中加入const,以便永远不会意外更改函数参数(以查找像if(a=5)这样的错误)。 - Casey Kuball

4

'const int'参数中的'const'应该被编译器忽略。然而,声明和定义之间的差异至少是不好的风格。


我真的希望标准至少要求在包含不匹配的单元编译期间输出诊断消息。 - D.Shawley
6
我完全不同意 - int参数的const特性是一种实现细节,对函数调用者没有任何影响。没有必要在接口中加入实现细节。 - SCFrench
+1 给 @SCFranch。毫无疑问,在声明中加入“const”只会暴露无用的实现细节。 - Johannes Schaub - litb
“int参数的const属性是一种实现细节,对调用者没有任何影响”——这并不完全正确。在某些情况下,它可能会有帮助。例如,通过引用传递大型对象。作为调用者,我很乐意知道参数的const属性。这可以快速告诉第三方程序员/用户哪些参数是输入,哪些参数同时是输入和(多个)输出。 - h9uest
这不是我想说的。请参考https://dev59.com/OGUp5IYBdhLWcg3wZHAc,了解我想表达的更详细信息。 - SCFrench

1

我的理解是这样做是允许的,因为对调用者几乎没有影响。这不是函数是const,而是一个参数,并且您在定义中进行了添加。因此,您实际添加的const仅影响实现

请参见问题


对于调用者来说并不重要,因为你正在复制传递的参数。如果参数是按引用传递的,那就不同了。 - Dusty Campbell

0

请参考Alexandrescu的《C++编程规范》第15条。他认为这种写法是可行的。const关键字可以防止函数实现者改变输入变量。在我的看法中,函数内部的变量应该保持不变。如果需要一个变量,请声明一个局部变量。优化器会帮你处理掉它。

int temp = a;

由于这种方法在这里不起作用,我建议我的班级在原型和实现中都使用(const int a)。我非常喜欢使用适用于所有编译器的技术。


0

我总是会在声明和定义中都使用 const。这样做可以减少问题,因为签名将匹配。


0

const对于函数void func1(const int)没有任何影响,因为int是按值传递的 --- 一个int的副本被创建,并且该副本只在函数调用期间存在。无论您是否更改该副本都不会影响任何事情。

您可能会因为void func2(const char*)中的const有意义而感到困惑。但您必须认识到这与void func3(char* const)不同。在前者中,指向的字符不能更改;在后者中,指针是(不相关的)'const'。


我并不困惑;-)我的示例中没有任何指针,我想知道为什么这种技术在一个编译器上有效而在另一个编译器上无效。请阅读我链接的问题或尝试自己的示例。这是一种有用的技术,因为它有助于捕捉函数本身的错误,但应该对调用者没有任何影响。 - Chris Huang-Leaver

-2

是的,const int 和 int 不同。


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