函数重载和 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
的函数不会假装相同,除非它们具有相同的参数列表。
为什么在一个编译单元中定义多个同名函数不违反一个定义规则?编译器如何识别违反 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
的函数不会假装相同,除非它们具有相同的参数列表。