【问题标题】:C++11 std::function const overload ambiguityC++11 std::function const 重载歧义
【发布时间】:2015-08-01 21:29:11
【问题描述】:

我在一个较大程序的某个部分遇到问题,我想说的不是模棱两可的东西被 g++clang++ 认为是模棱两可的

#include <functional>
#include <string>

struct Foo {
    Foo(int) {}
    Foo(std::string) {}
    operator int () const { return 42; }
    operator std::string () const { return ""; }

    void foo(std::function<void(Foo&, int)>f);
    void foo(std::function<void(const Foo&, int)>f) const; // xxx

    void foo(std::function<void(const std::string&, Foo&)>f);
    void foo(std::function<void(const std::string&, const Foo&)>f) const;

    void bar() const {
        this->foo([](const Foo&, int){}); // xxx
    }
};

我希望在 bar 中调用的 ::foo 能够明确解析为标有 xxx 的 const 版本,而两个编译器都抱怨重载解析不明确:

g++ -std=c++11 -c -Wall amb.cpp 
amb.cpp: In member function ‘void Foo::bar() const’:
amb.cpp:18:40: error: call of overloaded ‘foo(Foo::bar() const::<lambda(const Foo&, int)>)’ is ambiguous
         this->foo([](const Foo&, int){});
                                        ^
amb.cpp:12:10: note: candidate: void Foo::foo(std::function<void(const Foo&, int)>) const
     void foo(std::function<void(const Foo&, int)>f) const;
          ^
amb.cpp:15:10: note: candidate: void Foo::foo(std::function<void(const std::basic_string<char>&, const Foo&)>) const
     void foo(std::function<void(const std::string&, const Foo&)>f) const;
          ^

为什么不清楚我要调用哪个版本?我该如何解决这个问题?

【问题讨论】:

  • 查看std::function 的构造函数约束。这两个命名的重载都是允许的。两者都产生用户定义的转换,没有一个比另一个更好。
  • @KerrekSB:我不确定我是否理解。标记的版本有一个 相同 std::function 参数声明,其他版本没有。为什么这也被视为用户定义的转换?
  • 您的函数参数是一个 lambda 表达式,而不是 std::function&lt;void(const Foo&amp;, int)&gt; 类型的表达式。所以你需要一个转换。

标签: c++11 overloading std-function const-correctness


【解决方案1】:

问题是从 lambda 传递到 std::function 对象(尤其是参数类型)时,某些类型信息会丢失。

更具体地说,例如一个接受 std::function&lt;void(int)&gt; 的函数和另一个接受 std::function&lt;void(double)&gt; 的函数对于接受 int 参数的 void lambda 被认为是同样好的重载。

例如

#include <functional>

void foo(std::function<void(int)>f);
void foo(std::function<void(double)>f);

void bar() {
    foo([](int){});
}

bar 中的调用被认为是模棱两可的

amb2.cpp: In function ‘void bar()’:
amb2.cpp:8:18: error: call of overloaded ‘foo(bar()::<lambda(int)>)’ is ambiguous
     foo([](int){});
                  ^
amb2.cpp:4:6: note: candidate: void foo(std::function<void(int)>)
 void foo(std::function<void(int)>f);
      ^
amb2.cpp:5:6: note: candidate: void foo(std::function<void(double)>)
 void foo(std::function<void(double)>f);
      ^

解决方法

一种解决方案是显式创建正确的std::function 对象:

this->foo(std::function<void(const Foo&, int)>([](const Foo&, int){}));

【讨论】:

  • 你有更多关于这是为什么的信息吗?我遇到了这个问题,并花了很长时间试图弄清楚,因为没有太多关于它的信息。
  • @Benjamin:抱歉,我不是语言律师,我从未尝试过实现 C++ 前端。不幸的是,闭包作为 C 中的一个概念缺失,因此它们有点“固定”C++ 和非常奇怪的对象(例如,每个 lambda 定义都是它自己的唯一类型,即使它具有相同的接口)。由于语言中的所有其他固定功能,这些缺陷可能是不可避免的......
【解决方案2】:

您可以让构造函数采用std::string 参数explicit。这样编译器就不能进行从std::stringFoo的隐式转换。

【讨论】:

  • @6502 那么你将继续有歧义,除非你能以其他方式解决它。顺便说一句,我认为隐式转换的需要是一种设计味道,它会使代码更难阅读和理解。
  • 随着我答案的改变,代码可以工作(并且也可以在完整的程序中工作)。与 C++ 类型进行隐式转换的需求是必不可少的(它基本上是支持 JSON 数据的“无类型”值的实现,因此隐式从/到转换是为了使对象更可用)。 JSON 和 C++ 之间肯定存在某种阻抗不匹配,但在这种特定情况下,我不会称其为异味。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多