【问题标题】:Is the const value parameter in definition but not declaration really C++?定义中的 const value 参数而不是声明中的参数真的是 C++ 吗?
【发布时间】:2010-10-20 14:16:47
【问题描述】:

这类似于(但不同于)this question

这里有一些简单的测试代码来说明我在 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?

【问题讨论】:

  • 您是否向 Oracle 发送了错误报告?

标签: c++ solaris language-features


【解决方案1】:

请参阅 Alexandrescu #15 撰写的 C++ 编码标准。这应该按照他的方式工作。 const 防止函数的实现者更改输入变量。 IMO 变量应在函数内保持不变。如果您需要一个变量,请声明一个。优化器会为你摆脱它。

int temp = a;

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

【讨论】:

    【解决方案2】:

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

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

    但是有const/volatile修饰符可以参与重载,正如标准稍后提到的那样:

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

    【讨论】:

    • +1,对我来说属于“我理解逻辑,但似乎它只会使语言变得更奇怪”的类别。
    • 这在复制参数的地方很有意义:const int 可以复制到 int,因为它们是分开存储的。但是,const int* 不能复制到 int*,因为 const 是共享的。
    • @JaredPar,这里的一个用例是更改 API 以确保您的实现可以修改值参数是没有意义的。但是,您可能希望将 const 放入您的实现中,这样您就不会意外更改函数参数(以发现像 if (a = 5) 这样的错误)。
    【解决方案3】:

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

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

    【讨论】:

    • 我并不困惑 ;-) 我的示例中没有任何指针,我想知道为什么这项技术适用于一个编译器而不是另一个。阅读我链接到的问题或自己尝试示例。这是一种有用的技术,因为它有助于捕获函数本身的错误,但应该对调用者没有任何影响。
    【解决方案4】:

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

    看到这个question

    【讨论】:

    • 这对调用者无关紧要,因为您正在复制传递的参数。如果参数是通过引用传递的,那就不一样了。
    【解决方案5】:

    编译器应忽略“const int”参数中的“const”。但是,声明和定义之间的区别至少可以说是糟糕的风格。

    【讨论】:

    • 我真的希望标准在编译包含不匹配的单元期间至少需要一个诊断消息。
    • 我完全不同意 - int 参数的 const 是一个对调用者绝对没有影响的实现细节。无需将接口与实现细节混为一谈。
    • +1 到@SCFranch。当然,将“const”放入声明中只会暴露无用的实现细节
    • @SCFrench “int 参数的常量是一个对调用者绝对没有影响的实现细节”——这不是真的。在某些用例中,它可能会有所帮助。例如,大对象是通过引用传入的。作为调用者,我很高兴知道参数的 const-ness。这可以快速告诉第三方程序员/用户哪些参数是输入,哪些是输入和(多个)输出。
    • 我说的不是这个。请参阅stackoverflow.com/questions/15297644/… 了解我想说的更详细的说明。
    【解决方案6】:

    是的,const int 与 int 不同。

    【讨论】:

      【解决方案7】:

      我总是在声明和定义上匹配const。这将减少任何问题,因为签名会匹配。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-03-16
        • 2018-12-23
        • 1970-01-01
        • 1970-01-01
        • 2012-04-13
        • 2019-01-15
        • 1970-01-01
        相关资源
        最近更新 更多