【问题标题】:How does C++ pick which overloaded function to call?C++ 如何选择调用哪个重载函数?
【发布时间】:2010-09-27 23:39:03
【问题描述】:

假设我有三个班级:

class X{};
class Y{};
class Both : public X, public Y {};

我的意思是说我有两个类,然后是扩展这两个类的第三个类(多重继承)。

现在说我在另一个类中定义了一个函数:

void doIt(X *arg) { }
void doIt(Y *arg) { }

我用两者的实例调用这个函数:

doIt(new Both());

这会导致编译时错误,说明函数调用不明确。

除此之外,C++ 编译器在哪些情况下判断调用不明确并抛出错误(如果有)?编译器如何确定这些情况是什么?

【问题讨论】:

  • 是的,但我没有使用 C++ 编译器
  • codepad.org for gcc 和 comeaucomputing.com/tryitout for comeau 是两个很好的快速测试网站。
  • 啊,键盘是我要找的,谢谢!我也在寻找有关该主题的解释,而不仅仅是“如果我运行它,它是如何工作的”
  • @Claudiu,我为我的答案添加了指向源材料的链接。
  • doIt(string a) 和 doIt(char *a) 也有类似的问题。这不是“试一试”的问题。即使使用“char *”,它也会调用 doIt(string a),所以这是一个有效的问题。遇到这个是因为我正在调用一个使用字符串类型的函数,而该函数又调用了 char * 版本的 teh 函数。然而,字符串输入函数无限期地递归,直到它炸毁堆栈。

标签: c++ inheritance programming-languages function multiple-inheritance


【解决方案1】:

简单:如果它不明确,那么编译器会给你一个错误,迫使你选择。在你的 sn-p 中,你会得到一个不同的错误,因为 new Both() 的类型是指向 Both 的指针,而 doIt() 的两个重载都按值接受它们的参数(即它们不接受指针)。如果您将doIt() 更改为分别采用X*Y* 类型的参数,编译器会给您一个关于模糊函数调用的错误。

如果您想显式调用其中一个,请适当地转换参数:

void doIt(X *arg) { }
void doIt(Y *arg) { }
Both *both = new Both;
doIt((X*)both);  // calls doIt(X*)
doIt((Y*)both);  // calls doIt(Y*)
delete both;

【讨论】:

  • +1 尽管使用了 C 风格转换(和手动内存管理)
【解决方案2】:

我在 gcc 中遇到了这个错误:

jeremy@jeremy-desktop:~/Desktop$ g++ -o test test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:18: error: call of overloaded ‘doIt(Both&)’ is ambiguous
test.cpp:7: note: candidates are: void doIt(X)
test.cpp:11: note:                 void doIt(Y)

【讨论】:

    【解决方案3】:

    这是使用boost::implicit_cast的完美示例:

    void doIt(X *arg) { }
    void doIt(Y *arg) { }
    
    doIt(boost::implicit_cast<X*>(new Both));
    

    与其他解决方案(包括 static_cast)不同,如果无法从 Both* 隐式转换为 X*,则转换将失败。这是通过一个技巧完成的,最好用一个简单的例子来展示:

    X * implicit_conversion(X *b) { return b; }
    

    这就是boost::implicit_cast,只是它是一个模板,告诉它b 的类型。

    【讨论】:

    • @litb 你能解释一下 static_cast 和 implcit_cast 有什么区别
    • 您可以使用 static_cast 进行向下转换。对于implicit_cast,情况并非如此。 static_cast 基本上允许您进行任何隐式转换,以及任何隐式转换的反转(有一些限制。如果涉及虚拟基类,则不能向下转换)。但implicit_cast 接受隐式转换。如果 T 只有 U 的显式构造函数,则没有向下转换,没有 void*->T*,没有 U->T。
    【解决方案4】:

    编译器会进行深度搜索,而不是选择重载的广度搜索。 完整答案在Herb Sutter's exceptional C++,可惜我手头没有这本书。

    编辑:现在拿到手头的书 这叫深度优先规则叫"The Interface Principle"

    X 类的接口原则, 所有功能,包括免费 功能,即(a)“提及” X 和 (b) “随附” X 在逻辑上是 X 的一部分,因为它们 构成 X 接口的一部分。

    但有一条称为"Koenig Lookup" 的次要规则让事情变得更加困难。

    引用:“(简化):如果您提供 类类型的函数参数(这里 x,类型 A::X),然后查找 正确的函数名编译器 考虑匹配的名称 命名空间(此处为 A)包含 参数类型”-Herb Sutter, 出色的 C++,p120

    【讨论】:

      【解决方案5】:

      您需要将参数显式转换为 x 或 y

      doIt(new Both());

      所以添加...

      (X *) 或 (Y *)

      喜欢...

      doIt((X *)new Both());

      【讨论】:

      • 你的意思是 (X *) 和 (Y *),我会为你解决这个问题
      • 另外,你应该支持 c++ 风格的演员表
      【解决方案6】:

      AFAIK C++ 编译器将始终选择它可以在编译时确定的最接近和最具体的匹配。

      但是,如果您的对象是从两者派生的,我认为编译器应该给您一个错误或至少一个非常严重的警告。声明顺序应该无关紧要,因为这是关于子类型关系,并且第一个对象不是比另一个对象“更多的子类型”。

      【讨论】:

        猜你喜欢
        • 2019-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多