2016-01-20 69 views
4

此代码使用声明特定重载函数

struct Foo{ 
    void f(){ 
     f(0); 
    } 
private: 
    void f(int){} 
}; 

struct Bar : private Foo{ 
    using Foo::f; 
}; 

int main() { 
    Bar b; 
    b.f(); 
} 

fails to compile因为Foo::f(int)private。我对Foo::f(int)不感兴趣,我只是想Foo::f()这是public,所以我觉得应该有办法做到这一点。

有一些解决方法,我能想到的:

  1. 重命名Foo::f(int)Foo::p_f(int),但这是多余的,不允许超载分辨率为f
  2. 实施Bar::foo(){Foo::f();}成为很多复制/粘贴多个publicf s
  3. 继承public ly从Foo邀请UB,因为~Foo()不是virtual(并且不是s upposed是)
  4. 使所有f小号public使得它太容易不小心打破FooBar

有没有办法说using public Foo::f;?或者使用其中一个解决方法,而没有相关的缺点?

+0

没有在基类中的虚拟析构函数不是自动UB。如果你有一个实际指向(或引用)子类实例的基类的指针(或引用),并且你销毁该实例,那只有UB。在你显示的(不可否认的简单)代码中,没有公共继承的UB。 –

+3

这些具有相同的名字实际上是一个坏主意,它的原因包括这个使用声明混乱。我建议阅读关于“非虚拟接口”,这也有助于减少这种混乱。 –

+0

为什么不让'Foo :: f(int)'为受保护的方法?任何使用'Bar'或'Foo'的代码仍然只能访问'Foo :: f(void)',所以接口是一致的。 – Archimaredes

回答

0

如果您f(int)应该是private,并且永远不会成为一个公共API的一部分,那么你不应该在乎重命名它fimpl

struct Foo{ 
    void f(){ 
     fimpl(0); 
    } 
private: 
    void fimpl(int){} 
}; 

如果,另一方面,f(int)是公共API的通用版本,并且您还希望使用特定值的单个便利包装,您可以使用提供默认参数并使其成为f(int)成员。

struct Foo{ 
    void f(int = 0){} 
}; 

最后,如果你想在多个命名功能,提供了一定的整数值,那么我建议从Foo重命名这些包装

struct Foo{ 
    void f(int) {} // general interface 
    void sensible_name1() { f(0); } 
    void sensible_name2() { f(1); } // etc. 
} 
+1

这是错误的。 [“在* using-declaration *中没有指定构造函数,所有引入声明的所有成员都可以访问。\ [... \]特别是,如果派生类使用* using-declaration *访问基类的成员,成员名称应该是可访问的,如果名称是重载成员函数的名称,则所有命名的函数都应该可访问。“](http://eel.is/c++draft/ namespace.udecl#17) –

+0

@TC更新.. – TemplateRex

+0

@ T.C。有趣的是,措辞包含了一点含糊之处。考虑一下'Bar'有'void f(int)= delete;'的情况。然后,因为会有冲突,规范说明了与'Foo :: f(int)'过载相对应的名称* not *引入。第一条规则说明没有给出该名称的访问冲突。但是重载函数的最后一句话说,必须给所有名字的访问冲突提供潜在的机会。我不认为这是想要的:如果Foo中的void f(int)'是名称'f'的唯一组成部分,则规范要求不会发出访问冲突。 –

-1

你的代码目前派生Bar作为private。因此,Foo::f()私有方法Bar。您可以通过将您的类声明更改为struct Bar: public Foo来更改该声明。

有关背景信息,请参阅Derived Classes下的“私有继承”部分。具体而言,节说明:

[W]母鸡一类使用私有成员访问指示符从基站导出,所述基类的所有公共和受保护成员是作为派生类的私有成员访问(私人除非得到好处,否则基地成员永远不会进入)。