[dcl.fct.default]/10 中的明显矛盾

Apparent contradiction in [dcl.fct.default]/10

本文关键字:矛盾 dcl fct default      更新时间:2023-10-16

[dcl.fct.default]/10:

虚函数调用 (10.3) 使用 由静态类型确定的虚函数的声明 表示对象的指针或引用。覆盖函数 在派生类中不从 它覆盖的功能。[ 示例:

struct A {
virtual void f(int a = 7);
};
struct B : public A {
void f(int a);
};
void m() {
B* pb = new B;
A* pa = pb;
pa->f(); // OK, calls pa->B::f(7)
pb->f(); // error: wrong number of arguments for B::f()
}

—结束示例 ]

除了我上面强调的声明外,该段和例子对我来说很清楚。当我读到这篇文章时,我的印象是它与段落的第一部分相矛盾,即虚函数调用使用虚函数声明中的默认参数,这些参数由表示对象的指针或引用的静态类型确定。

它们并不矛盾,因为它们指的是不同的东西。

虚函数调用 (10.3) 在虚函数的声明中使用默认参数,这些参数由表示对象的指针或引用的静态类型确定。

这意味着以下内容:

#include <iostream>
struct Base
{
virtual void foo(int x = 3) { std::cerr << x << 'n'; }
};
struct Derived : Base
{
virtual void foo(int x = 4) { std::cerr << x << 'n'; }
};
int main()
{
Derived d;
Base* ptr = &d;
ptr->foo(); // prints 3, not 4
}

(现场演示)

派生类中的重写函数不会从它重写的函数中获取默认参数。

另一方面,这意味着以下内容:

#include <iostream>
struct Base
{
virtual void foo(int x = 3) { std::cerr << x << 'n'; }
};
struct Derived : Base
{
virtual void foo(int x) { std::cerr << x << 'n'; }
};
int main()
{
Derived d;
d.foo(); // doesn't compile; missing value for x
}

(现场演示)

我的印象是它与该段的第一部分相矛盾,即虚函数调用使用虚函数声明中的默认参数,该参数由表示对象的指针或引用的静态类型确定。

不。这意味着,如果你通过它的 Base 接口访问对象,你将获得 Base 的默认参数......如果你通过它的 Derived 接口访问对象,并且 Derived 没有自己的默认参数,你将不会得到 Base 的参数。同一枚硬币的两个截然不同的方面。

而且,公平地说,引用的标准文本给出了完全相同的例子。

这意味着派生类中的函数可以有自己的默认参数,也可以没有默认参数。

例如

struct A {
virtual void f(int a = 7)
{
std::cout << "F(" << a << ")" << std::endl;
}
};
struct B : public A {
void f(int a = 10)
{
std::cout << "F(" << a << ")" << std::endl;
}
};
B* pb = new B;
A* pa = pb;
pa->f(); // OK, calls pa->B::f(7)
pb->f(); // OK, calls pa->B::f(10)

在第一次调用中

pa->f(); // OK, calls pa->B::f(7)

根据指针的静态类型,使用了函数 f 声明的默认参数pa即函数在类 A 中的声明。

在第二个调用中

pb->f(); // OK, calls pa->B::f(10)

根据指针pb的静态类型,使用函数 F 声明的默认参数,即类 B 中函数的声明。