C++;:隐藏规则背后的基本原理 < C++ > 背后的原理是什么?

C++;:隐藏规则背后的基本原理 < C++ > 背后的原理是什么?,c++,language-design,hide,rationale,C++,Language Design,Hide,Rationale,如果这是一个有意义的特性,我认为也应该可以隐藏函数,而不使用相同的名称定义新函数:如下所示: class B : public A { hide void f(double); } 但这是不可能的 我认为这并不能简化编译器的工作,因为当您显式使用using指令时,编译器无论如何都必须能够取消隐藏函数: class B : public A { using A::f; void f(double); } // B::f(int) NOT 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
    
  • 那么,为什么会有隐藏规则呢



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

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

    我同意这个话题是如此频繁地讨论,可能隐藏实际上增加了C++程序员的“惊奇”量。
    关于这个问题的详细讨论可以找到……

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

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

    默认情况下,向基类添加方法不会影响派生类的重载解析

    而且,您也不会因为某个错误而与重载解决冲突,即使用say参数
    false
    将您的调用指向具有形式参数
    void*
    的基类方法。这样的事情


    欢呼和H.T.P/> < P>我确信我看到过这个案例是由C++ BigWigg提供的,不确定哪个:

    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++中的基类是脆弱的,因为他们是,没有这个也:-)/P> 不需要“hide”关键字,因为在派生类中不重载“f”时,也没有类似的情况可以从基中隐藏“f”


    顺便说一句,我选择了类型,
    char
    故意不一致。使用
    int
    vs
    unsigned int
    可以得到更微妙的情况,而不是
    int
    vs
    char
    可能是因为模板专门化。我举一个例子:

    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(); }
    };
    

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

    #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;
    }
    

    虽然这一决定并不是基于简化编译器编写器的寿命,但编译器仍然需要取消隐藏的论点是无效的。当编译器使用using解析类时,它将所有
    A::f
    放入类的内存表示中。当它尝试解析一个调用时,只需返回到需要的位置,直到找到标识符的第一个匹配项。不需要通过可能的多个路径不断返回以将所有可能的标识符引入范围。成员方法将隐藏名称空间级函数这一事实也是如此……当然,在C++11中,可以通过
    =delete
    隐藏而不定义新函数。有一种隐藏基类函数的机制。使用
    A类:受保护的B{…}而不是
    public
    。抱歉打了一匹死马。我很困惑,实际上你是唯一一个谈论对其他作用域隐藏函数的一致性的人!我仍然认为这主要是为了避免意外。除了,在您的示例中,模板类B中绝对没有任何东西会隐藏在模板类a中声明的方法f。。。这个错误并不是因为f(void)隐藏在B中(为什么它在B中比在B中隐藏得更多,因为没有专门化)。你的问题是f(void)根本没有在A中声明:没有什么可以隐藏的。
    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;
    }
    
    #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);