通过“函数参数”中的 const 类型在派生类中具有不同函数参数的虚拟函数会破坏虚拟机制吗? [复制]

Posted

技术标签:

【中文标题】通过“函数参数”中的 const 类型在派生类中具有不同函数参数的虚拟函数会破坏虚拟机制吗? [复制]【英文标题】:Virtual functions with varying of function parameters in derived class by const type in "function parameter" will break virtual mechanism? [duplicate] 【发布时间】:2018-04-18 18:32:24 【问题描述】:

一位 C++ 大师告诉我们,在派生类中使用 const 改变函数参数类型会破坏虚拟调用机制。

我尝试了一个简单的程序(原谅非标准代码,纯粹为测试而编写),但事实证明并非如此。 通过const值改变函数参数不会破坏虚机制,

是否有任何原因和文档指向这种行为?

使用 VS 2012 编译器和最新的 g++ 编译器记录的行为。

#include <iostream>
using namespace std;


class Base

public:
Base() cout<<"base"<<endl;
virtual ~Base() cout<<"dest base"<<endl;
virtual void test(const int x) cout << "base test"<<"x = " << x<<endl;
;

class Derived : public Base

public:
Derived() cout<<"derived"<<endl;
virtual ~Derived() cout<<"dest derived"<<endl;
virtual void test(int x) cout << "derived test"<<"x = " << x<<endl;
;
int main() 

    Base *b = new Derived();
    b->test(10);
    delete b;
    return 0;

输出:

base
derived
derived testx = 10
dest derived
dest base

【问题讨论】:

您的大师很可能在谈论foo(int&amp;)foo(const int&amp;) 之类的东西。 请注意,一般来说,演示行为的代码并不能证明该行为。例如,我可以在我的平台上构建一个示例,该示例使用对int 的悬空引用,这似乎可以工作。但这并不能证明它通常有效,只能证明我对我未定义的行为感到(不)幸运。由于存在未定义的行为,显示某物以某种方式表现并不能证明它总是会以这种方式表现。最好转而依赖可靠的文档,最好是标准本身。 可能被Top-level const doesn't influence a function signature 或Why does a function declaration with a const argument allow calling of a function with a non-const argument? 欺骗;另请参阅更易搜索的const qualifier disappears from pure virtual function 它不会“破坏”任何东西;只是覆盖函数必须具有与其覆盖的函数相同的参数列表。正如派生类中的void f(int) 不会覆盖基类中的virtual void f(double)void f(int&amp;) 也不会覆盖virtual void(const int&amp;) 【参考方案1】:

*** cv 限定符不是函数签名的一部分,它们被简单地忽略。

[dcl.fct]/5

在生成参数类型列表后,任何修改参数类型的*** cv 限定符都会在形成函数类型时被删除。

【讨论】:

【参考方案2】:

您的C++ guru 是错误的(如果您理解它们,大师们往往会用神秘的信息说话)。参数类型本身的Const-qualifier 根本不是函数签名的一部分。

例如,void foo(int* const );void foo(int* ) 没有什么不同。请注意,它与间接对象的 const 限定不同,例如 void foo(const int* )void foo(int* ) 不同。

在您的特定情况下,void test(int x)void test(int const x) 相同

【讨论】:

【参考方案3】:

void test(int) != void test(int) const 会“中断”虚拟通话。 和 void test(int&amp;) != void test(const int&amp;) 会“中断”虚拟通话。

void test(int)void test(const int) 声明相同,不会“中断”虚拟调用。

【讨论】:

一切正常。感谢您的回答【参考方案4】:

std::decay 的等价物发生在函数的参数类型上。 (实际上是相反的,std::decay 是根据函数参数所做的建模。)

outermost const 将从签名中删除。最外层,将类型视为由不同类型组成的信封。指向 const int 的指针与指向 int 的指针是不同的类型,并且会导致不同的函数签名。 (有了指针,你可以把指针本身看成是外在的东西,它指向的是“内”,没有被修改。)

const int - 变成了 int int * 保持不变并保持 int * const int * 保持不变 const int * - const 在 int 上,而不是指针上,只有最外层的 const 被删除

int const * 保持不变并保持 int const * - const 在 int 上,而不是在指针上,只有最外面的 const 被删除。 (注意,这与const int *的含义100%相同)

int * const - 更改为 int * - 因为 const 限定了最外层的指针

int * const * const 变为 int * const * - 外部 const 被丢弃在外部指针上,内部 const 不被丢弃在内部指针上。 const int * const * const 变为 const int * const * - 外部 const 被丢弃在外部指针上,内部 const 不被丢弃在内部指针上,并且 const 也保留在最内部诠释 MyTemplate&lt;const T&gt; - 不变,保持 MyTemplate&lt;const T&gt;,因为 const 不在外部类型上,而是位于模板参数中

所以是的, const 确实会影响类型,但不会像您尝试的那样简单。仅当它包含在一个类型内部时,不影响最外层的类型。

如果您从从右到左读取类型,它会有所帮助。如果类型说明符中最右边的东西是 const,它总是被丢弃(例如 int * const)。如果最左边的东西是 const,那么只有当它限定的东西是类型中最右边的东西时才被删除(例如const int,最左边的东西是 const 并且它影响它右边的 int 和它右边的 int 是类型中最右边的东西。)(示例 2:const * int 没有被删除,因为最左边的 const 修改了它右边不是类型中最右边的东西。)

【讨论】:

这是一个很好的视角。另外:这就是为什么我在我自己的所有代码中都严格执行从右到左的类型! int const *const 必须如此。 当我决定编码风格时,这也是我的规则。是的,const 的规则是修改左边的东西除非左边没有东西,在这种情况下它做右边的东西。将事物向右修改是规则的例外,并且使从右到左大声朗读更加麻烦。【参考方案5】:

他是对的。只是看到警告才发现的。

在这种情况下:VS2008 之前的编译器会在这种情况下破坏虚拟机制。

后来的编译器给出警告 C4373: virtual function overrides '%$pS', 当参数仅由 const/volatile 限定符不同时,以前版本的编译器没有覆盖

在这里找到文档https://msdn.microsoft.com/en-us/library/bb384874.aspx

【讨论】:

“他是对的” 并没有从 '一个编译器在某个时间点出现错误' 得出(现在发出一个相当虚假的警告)。当然,这是一个有用的事实。然而,这不是一个答案。 “C++ 大师”指的是标准,而不是一个编译器的一个版本。 @underscore_d 如果此编译器实际用于此设置,那么大师当然是对的,但 OP 忽略此细节是错误的。 加起来,感谢您提供大量好的答案! 写可移植代码,还要考虑版本? 如果它是一个不真实的陈述,它就是不正确 - 即使它碰巧在一个特定的、破碎的上下文中按预期运行。充其量,这并不是“在所有情况下都 100% 错误”。

以上是关于通过“函数参数”中的 const 类型在派生类中具有不同函数参数的虚拟函数会破坏虚拟机制吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

函数的重载重写与隐藏

如何在派生类中初始化需要构造函数参数的成员对象?

派生类中的虚函数

在 TypeScript 中为具有类型变量的抽象类中的派生类分配泛型类型

抽象类中的纯虚函数,返回类型为基/派生类型

如何影响派生类中受保护的基变量