【问题标题】:Why is a program rejected as ambiguous that could be resolved by overload resolution?为什么一个程序被拒绝为可以通过重载解决方案解决的模棱两可?
【发布时间】:2015-12-22 14:03:59
【问题描述】:

以下程序因不明确而被 gcc 拒绝:

struct Aint 
{
    virtual void foo(int);
};

struct Astring 
{
    virtual void foo(std::string);
};

struct A: public Aint, public Astring {};

int main()
{
  std::string s;

  A a;
  a.foo(s);

  return 0; 
}

> vt.cpp: In function ‘int main()’: vt.cpp:13:9: error: request for
> member ‘foo’ is ambiguous
>        a.foo(s);
>          ^ vt.cpp:5:34: note: candidates are: virtual void Astring::foo(std::__cxx11::string)
>      struct Astring {virtual void foo(std::string);};
>                                   ^ vt.cpp:4:31: note:                 virtual void Aint::foo(int)
>      struct Aint {virtual void foo(int);};

Clang 出于同样的原因一直拒绝该程序:

clang -std=c++1y -c vt.cpp 

vt.cpp:13:9: error: member 'foo' found in multiple base classes of different types
      a.foo(s);
        ^
vt.cpp:4:31: note: member found by ambiguous name lookup
    struct Aint {virtual void foo(int);};
                              ^
vt.cpp:5:34: note: member found by ambiguous name lookup
    struct Astring {virtual void foo(std::string);};

我不完全确定我是否正确理解了第 10.2 节中的查找规则,因此我将通过以下步骤中的规则来计算查找集 S(foo, A):

1. A does not contain `foo`, so rule 5 applies and S(foo, A) is initially empty. We need to calculate the lookup sets S(foo, Aint) and S(foo, Afloat) and merge them to S(foo, A) = {}
2. S(foo, Aint) = {Aint::foo}
3. S(foo, Afloat) = {Afloat::foo}
4. Merge S(foo, Aint) = {Aint::foo} into S(foo, A) = {} to get S(foo, A) = {Aint::foo} (second case of 6.1)
5. Merge S(foo, Afloat) = {Afloat::foo} into {Aint::foo}. This create an ambiguous lookup set because of rule 6.2

结果集是无效集,因此程序格式错误。

我想知道为什么这个程序这么早就被拒绝了。在这种情况下,编译器应该很容易进行重载解析,因为两个函数具有相同的名称但不同的签名,因此没有真正的歧义。是否存在未完成的技术原因,或者是否有其他原因可以接受不正确的程序?有人知道这么早拒绝这些计划的原因吗?

【问题讨论】:

  • 你在 clang 上试过了吗?似乎编译器错误
  • @AngelusMortis 我添加了 clang 3.6 的输出。根据标准中的查找规则,我认为是正确的。
  • 只是重现了同样的结果,不是编译器。
  • @simplicisveritatis ,不,如果将 std_string 替换为例如 char*,结果是相同的。
  • 这是正确的行为。引用 Bjarne,"in C++, there is no overloading across scopes".

标签: c++ language-lawyer name-lookup


【解决方案1】:

In C++, there is no overloading across scopes – 派生类作用域也不例外。请refer 了解更多详情。 无论如何,您的示例可以通过“使用”关键字指定要使用两个版本的 foo 来改进。请参见下面的示例。

示例程序:

#include <iostream>
#include <string>

struct Aint 
{
     void foo(int){std::cout<<"\n Aint";}
};

struct Astring 
{
     void foo(std::string){std::cout<<"\n Astring";}
};

struct A: public Aint, public Astring {
    using Aint::foo;
    using Astring::foo;
    };

int main()
{
  std::string s;

  A a;
 a.foo(s);

  return 0; 
}
output:Astring

【讨论】:

  • 原始示例中有可变参数模板。像template&lt;typename T&gt; struct A {virtual void foo(T);}; template&lt;typename... T&gt; struct B: public A&lt;T&gt;... { /* using A&lt;T&gt;...; */ }; 这样的东西。在这个例子中(这不是我的代码,我在 SO 上看到过)你不能编写 using 声明,因为它不适用于模板参数包扩展(参见 groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/…)。
  • @Jens :我理解你的意思。请你发布完整的原始示例以便更好地理解
  • 我创建了一个新问题来解决这个问题,因为我认为它不适合这里:stackoverflow.com/questions/34418749/…
  • 谢谢詹斯。我会调查的:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-08
  • 2016-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-20
相关资源
最近更新 更多