C++:隐藏规则背后的基本原理

Posted

技术标签:

【中文标题】C++:隐藏规则背后的基本原理【英文标题】:C++: rationale behind hiding rule 【发布时间】:2011-06-17 18:25:43 【问题描述】:

C++ 中hiding rule 背后的基本原理是什么?

class A  void f(int); 
class B : public A  void f(double);  // B::f(int) is hidden

如果这是一个有意义的功能,我认为它也应该可以隐藏函数而不定义具有相同名称的新函数:像这样:

class B : public A  hide void f(double); 

但这是不可能的。

我认为它不会简化编译器的工作,因为当您明确使用 using 指令时,编译器必须能够取消隐藏函数:

class B : public A  using A::f; void f(double);  // B::f(int) NOT hidden

那么,怎么会有隐藏规则呢?


嗯,这三个答案似乎都不错,并且显示了隐藏规则的不同理由。我不确定我应该接受哪个答案。

【问题讨论】:

虽然该决定不是基于简化编译器编写者的生活,但编译器仍需要取消隐藏的论点是无效的。当编译器使用 using 解析类时,它会将所有A::f 带入类的内存表示中。当它试图解析一个调用时,它只需要返回到需要的地方,直到它找到第一次出现的标识符。没有必要通过可能的多条路径继续返回以将所有可能的标识符纳入范围。成员方法将隐藏命名空间级别函数的事实也是如此...... AFAIK,隐藏而不定义新函数在 C++11 中通过=delete 是可能的。 一种隐藏基类函数的机制。使用class A : protected B ... ; 而不是public。对不起打死马了。 【参考方案1】:

这是一个棘手的问题,但显然这个想法是这种隐藏功能有助于在更改基类时避免细微的错误(否则可能会“窃取”以前由派生类处理的调用)。基类的变化仍然会影响派生类的编译结果,所以我认为我不能 100% 理解这个解释。

我同意这个话题被如此频繁地讨论,可能隐藏实际上增加了 C++ 程序员的“惊喜”数量。

关于这个问题的详细讨论可以找到here...

【讨论】:

是的,我刚刚花了 2 个小时才惊讶于为什么当 Internet 上的示例运行良好时编译器看不到我的函数。【参考方案2】:

我不知道最初的理由,但因为隐藏或不隐藏是同样糟糕的选择 wrt。对于函数,我猜测其基本原理是具有统一规则:与嵌套花括号范围中定义的名称相同。

隐藏在某些方面对你有帮助。

向基类添加方法默认不会影响派生类的重载决议。

并且您不会因为某些意外将带有参数false 的调用引导到带有正式参数void* 的基类方法而与重载解决方案发生冲突。这样的事情。

干杯,

【讨论】:

令我感到困惑的是,您实际上是唯一一个谈论对其他范围隐藏功能的一致性的人!我仍然认为这主要是为了避免意外。【参考方案3】:

我确定我见过 C++ 大佬提供的这个案例,但不确定是哪个:

struct Base 
    void f(const Base&);
;

struct Derived : Base 
    using Base::f;
    void f(double);
;

int main() 
    Derived d;
    d.f('a'); // calls Derived::f

现在,将void f(int); 添加到Base,以及主要更改的含义 - 它调用Base::f,因为int 更适合char - 它是整数提升而不是标准转换。

尚不清楚程序员是否真的打算对基础进行更改以捕获带有char 的调用,因此要求using 明确意味着默认行为是更改不影响调用代码。我相信这是一个微不足道的呼吁,但我认为委员会认为 C++ 中的基类已经足够脆弱,没有这个 :-)

没有必要使用“hide”关键字,因为在 Derived 中没有重载时,没有类似的情况可以从 Base 中隐藏“f”。

顺便说一句,我选择了类型,char 故意不协调。您可以使用intunsigned int 而不是intchar 获得更微妙的案例。

【讨论】:

【参考方案4】:

隐藏基类的成员函数(名称相同但签名不同)的另一个原因可能是由于可选参数引起的歧义。考虑以下示例:

#include <stdio.h>

class A

public:
    int foo(int a, int b=0)
    
        printf("in A : %d, %d\n", a, b);
    
;

class B : public A

public:
    int foo(int a)
    
        printf("in B : %d\n", a);
        foo(a); //B:foo(a) will be called unless we explicitly call A:foo(a)
        foo(a, 1); // compile error: no matching function for call to B:foo(int&, int)
    
;


int main()

    B b;
    b.foo(10);
    return 0;

如果基类中的 foo 方法没有被隐藏,编译器将无法决定是调用 A::foo 还是调用 B::foo,因为以下行匹配两个签名:

foo(a);

【讨论】:

【参考方案5】:

可能的原因是模板专业化。我举个例子:

template <int D> struct A  void f() ;

template <> struct A<1>  void f(int) ;

template <int D>
struct B: A<D>

  void g()  this->f(); 
;

模板类 B 有一个方法f(),但除非您不创建类 B 的实例,否则您不知道签名。所以调用this-&gt;f() 随时“合法”。在您创建实例之前,GCC 和 CLang 都不会报告错误。但是,当您在 B&lt;1&gt; 实例上调用方法 g() 时,它们会指示错误。所以隐藏规则更容易检查你的代码是否有效。

我报告了我的示例中使用的代码的最后一部分。

int main (int argc, char const *argv[])

  B<0> b0; /* valid */
  B<1> b1; /* valid */

  b0.g(); /* valid */
  b1.g(); /* error: no matching function for call to ‘B<1>::f()’ */

  return 0;

【讨论】:

除了,在您的示例中,模板类 B 中绝对没有任何内容可以隐藏模板类 A 中声明的方法 f... 错误并非来自 f( void) 隐藏在 B 中(为什么它在 B 中比在 B 中隐藏得更多,因为没有专门化)。您的问题是 just 在 A 中根本没有声明 f(void) :没有什么可隐藏的。

以上是关于C++:隐藏规则背后的基本原理的主要内容,如果未能解决你的问题,请参考以下文章

Apple 检查返回值而不是错误的模式背后的基本原理是啥?

Prometheus Labeldrop 操作背后的基本原理

AngularJS 中属性规范化背后的基本原理

逗号语法:语句中悬挂逗号背后的基本原理是 SyntaxError

typedef vs struct/union/enum 背后的基本原理是啥,难道不能只有一个命名空间吗?

Misra 2012 背后的基本原理不允许在不同指针之间进行转换