函数重载和 ODR 如何共存? (C++)

How can function overloading and the ODR coexist? (C++)

为什么在一个编译单元中定义多个同名函数不违反一个定义规则?编译器如何识别违反 ODR 的代码和使用函数重载的代码?

在 C++ 中,定义 包括 参数类型(自 C++ 17 起,包括异常规范)。

因此,重载是可能的,因为即使名称相同,功能也不相同。

一次定义规则意味着每个重载函数必须定义一次。所以并不矛盾。每个重载函数在某些方面有所不同,例如参数的数量或类型、是否存在 c/v 限定符(在参数声明中或 class 成员函数本身)等等。

有时初学者会认为这些函数声明是重载函数的声明

void f( int a[100] );
void f( int a[10] );
void f( int a[] );
void f( int *a );

但是编译器将数组类型的参数隐式调整为数组元素类型的指针。

您可以在程序中包含所有这些(冗余)声明,但必须只定义函数一次。

所以上面的声明声明了同一个函数,其参数被编译器调整为int *.

类型

考虑到这些函数声明声明了两个重载函数

void f( int *a );
void f( const int *a );

(这里指针本身不是常量,是指针指向的数据是常量)

而这两个声明

void f( int x );
void f( const int x );

声明同一个函数,因为当编译器确定函数是否重载或相同时,const 限定符被丢弃。

当函数参数具有函数类型时,可能会出现同样的混淆。例如

void f( void g() );
void f( void ( *g )() );

编译器再次将函数类型的参数隐式调整为指向函数的指针。

这是一个演示程序

#include <iostream>

void f( void g() );
void f( void ( *g )() );

void g() { std::cout << "Hello Philippa Richter\n"; }

void f( void g() )
{
    g();
}

int main()
{
    f( g );
}

它的输出是

Hello Philippa Richter

注意函数f虽然被声明了三次,包括同时定义的声明,但它只定义了一次

单一定义规则适用于具有相同名称的事物;它适用于假装相同的事物。

两个名为 foo 的 class(在全局范围内声明,即在命名空间或 class 或函数之外声明)假装相同;他们必须是一样的。如果你改变了一个程序中的定义,你就是在欺骗编译器。 (这就像根据术语的特殊定义与某人争论,然后假装证明了某些适用于常规术语的东西。)

两个名为 bar 的函数不会假装相同,除非它们具有相同的参数列表。