通过“函数参数”中的 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&)
与foo(const int&)
之类的东西。
请注意,一般来说,演示行为的代码并不能证明该行为。例如,我可以在我的平台上构建一个示例,该示例使用对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&)
也不会覆盖virtual void(const int&)
。
【参考方案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&)
!= void test(const int&)
会“中断”虚拟通话。
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<const T>
- 不变,保持 MyTemplate<const T>
,因为 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 类型在派生类中具有不同函数参数的虚拟函数会破坏虚拟机制吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章