在函数内部重新声明一个函数

12

我偶然发现了一个奇怪的C++片段。我认为这是糟糕的代码。为什么有人会在函数内部重复声明函数?即使将类型签名更改为 unsigned int sum(int,int),它甚至可以编译并产生预期结果4294967294j。为什么这个会编译通过?

#include <iostream>
#include <typeinfo>

using namespace std;

int sum(int a, int b){
    return a + b;
}

int main()
{
    int sum(int, int); // redeclaring sum???
    int a = -1;
    auto result = sum(a, a);
    cout << result << typeid(result).name() << endl;
}

编辑:在我的电脑上编译通过了...但它是否是有效的C++代码?如果不是,为什么编译器(mingw 4.8.1)允许它通过?


2
你可以随意声明变量,信不信由你... int sum(int,int); int sum(int,int); int sum(int,int); int sum(int,int); 是合法的C++代码,并且需要这样才能允许前向声明。但是对我来说不太合理的是类型改变不仅可以接受,而且没有选择不同的名称... - IdeaHat
更加奇怪的是,您可以将函数的定义移动到main函数下面,在main函数中声明的sum函数可以返回一个无法转换为int的结构体,然后您可以调用aa ss = sum(d,e);,它会编译并运行。 - IdeaHat
将本地声明的返回类型更改为float,您会得到奇怪的结果。我认为这违反了ODR规则。 - jrok
@Markus,这是一个声明,改变返回类型是无效的。 - Luchian Grigore
1
@j 那个结果是我所期望的。函数的返回类型不像它的参数一样是其签名的一部分。由于 float sum(...) 遮蔽了 int sum(...),因此 result 的类型被推断为 float。但是当链接器寻找 sum(int, int) 时,它会忽略返回类型,并链接 int 版本,因为它是唯一可用的定义。在 x86 上返回值的方式是将 int 放入特定的寄存器中。调用者将该寄存器原封不动地复制到 result 中,从而将 float 重新解释为 int。总的来说,这几乎肯定是未定义行为和不可移植的。 - Nicu Stiurca
显示剩余3条评论
2个回答

6
有时候在块级作用域内重新声明一个函数是有必要的,例如如果您想设置默认参数。请看下面的代码示例:
#include <typeinfo>

using namespace std;

int sum(int a, int b){
    return a + b;
}

int main()
{
    int sum(int, int = -1 ); // redeclaring sum???
    int a = -1;
    auto result = sum(a, a);
    cout << result << typeid(result).name() << endl;

    result = sum(a);
    cout << result << typeid(result).name() << endl;
}

另一种情况是当您想从一组重载函数中调用具体的函数时。请考虑以下示例。
#include <iostream>

void g( int ) { std::cout << "g( int )" << std::endl; }
void g( short ) { std::cout << "g( short )" << std::endl; }

int main()
{
   char c = 'c';
   g( c );

   {
      void g( short );
      g( c );
   }
}

那很有用,虽然有点讨厌。 - rubenvb
第二种情况最好通过转换参数来处理。这最后一点非常讨厌 ;-) - rubenvb
@rubenvb 有时候强制类型转换看起来很糟糕,特别是当一个函数有很多参数的时候。 - Vlad from Moscow
重新声明函数将使得函数类型从调用大小中消失,并且在不同的函数重载混合时可能会隐藏错误。 - rubenvb

2

如果这是真正的代码,那么没有理由这样做。

但如果函数 sum 已在其他地方定义,则在 main 内部声明只能使其在 main 中可用。你无法在该翻译单元中任何其他地方使用它(除非当然你声明它)。因此,它可以将可见性限制为所需位置,但是,不可否认,它并不易读。

关于更改返回类型-那是非法的。虽然你没有看到 unsigned int 的任何问题,但如果你尝试

char sum(int, int); // redeclaring sum???

你会发现那里存在一个问题。


@Jarod42 返回类型不是签名的一部分。而且,那是非法的。 - Luchian Grigore
为什么在函数外重新声明会导致编译失败,而在函数内重新声明则可以成功编译?(https://ideone.com/1T91Bi) - Jarod42
@Jarod42出于同样的原因,(在函数中) { extern int i; extern short i; } 会失败,而{ extern int i; { extern short i; } }则被接受。如果声明具有相同的作用域,则不兼容的声明将在编译时被诊断。 - user743382

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