定义中而不是声明中的const值参数真的是C++吗?

问题描述 投票:0回答:8

这类似于(但不同)这个问题

这里有一些简单的测试代码来说明我在 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++ 编译它时,它会编译、链接并执行您在运行时所期望的操作。 您还可以添加 ++a;在 testy::lame() 中,编译器会抱怨更改只读变量(正如它应该的那样)。

但是,当我使用 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的书中查找了它,但找不到提到的这种技术(并不意味着它不存在!);那么这真的是一个编译器错误,还是只是一个在除 Solaris 之外的其他地方都有效的 hack?

c++ solaris language-features
8个回答
27
投票

这看起来像是

CC
中的编译器问题。 C++ 标准说(在 13.1 可重载声明中):

仅在是否存在 const 和/或 volatile 方面不同的参数声明是等效的。也就是说,在确定正在声明、定义或调用哪个函数时,将忽略每个参数类型的 const 和 volatile 类型说明符。

但是有

const
/
volatile
修饰符可以参与重载,正如标准随后不久提到的:

以这种方式仅忽略参数类型规范最外层的 const 和 volatile 类型说明符;隐藏在参数类型规范中的 const 和 volatile 类型说明符非常重要,可用于区分重载函数声明。


4
投票

“const int”参数中的“const”应该被编译器忽略。然而,至少可以说,声明和定义之间的差异是糟糕的风格。


1
投票

我的理解是这是允许的,因为这对调用者来说没有什么区别。它不是 const 函数,而是参数,并且您正在定义中进行添加。因此,您实际添加的 const 仅影响实现

看到这个问题


0
投票

我总是会在声明和定义上匹配

const
。这会减少任何问题,因为签名会匹配。


0
投票

const
void func1(const int)
对函数没有任何影响,因为 int 是按值传递的——创建了 int 的副本,并且该副本仅在函数调用期间存在。无论您是否更改该副本都没有任何影响。

您可能会对

void func2(const char*)
const
很重要这一事实感到困惑。但你必须认识到这与
void func3(char* const)
不同。前者是指向的字符,不能改变;在后者中,指针是(无关紧要的)“const”


0
投票

请参阅 Alexandrescu 的 C++ 编码标准 #15。按照他的说法,这应该有效。 const 防止函数的实现者更改输入变量。 IMO 变量应在函数内保持 const 状态。如果您需要一个变量,请声明一个。优化器会帮你去掉它。

int temp = a;

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


0
投票

您的代码是正确的;正如 Michael Burr 的回答所示,编译器存在问题。

以下两个函数声明是相同的:

void Print(int number);
void Print(const int number);  // Okay. Redeclaration of Print(int).

一般来说,大多数风格指南建议不要在函数声明中使用不必要的 const。因此,在上面的示例中,首选第一个声明。例如,请参阅:https://abseil.io/tips/109

但是,以下两个函数定义是不同的:

void Print(int number) {
  std::cout << number << std::endl;
}

void Print(const int number) {  // Error: Redefinition of Print(int).
  std::cout << number << std::endl;
}

这两个定义都定义了相同的函数声明

Print(int)
,因此第二个定义会由于单一定义规则而导致编译错误。然而,第二个定义(带有
const
的定义)更清晰:它表示参数的值不能在函数内部更改,并防止未来的开发人员意外修改参数的值。在这样一个简单的例子中,这可能并不重要,但一般来说,大多数风格指南都鼓励在函数定义中使用
const
;请参阅上面的 Abseil 库链接。

总体而言,通常推荐的方法是避免在函数声明中使用不必要的

const
,而是在函数定义中使用
const
。示例:

// Header file:
void Print(int number);

// Source file:
void Print(const int number) {
  std::cout << number << std::endl;
}

-2
投票

是的,const int 与 int 不同。

© www.soinside.com 2019 - 2024. All rights reserved.