c++ - 使用带有 std::optional 参数的 std::function 重载歧义

标签 c++ c++17

这是一个简单的示例程序:

using fn_string = function<void(const string&)>;
using fn_optional_string = function<void(const optional<string>&)>;

void foo(fn_string) { cout << "string" << endl; }
void foo(fn_optional_string) { cout << "optional string" << endl; }

int main()
{
    foo([&](const string&){ });
    foo([&](const optional<string>&){ }); // <-- ambiguous
    return 0;
}

它有 2 个重载 foo() -- 一个使用 string 的函数参数和另一个 optional<string> .

为什么第二次调用 foo()模棱两可?

有没有简单的方法来修复它?没有 Actor ?

更新

以上是我试图解决的以下现实世界问题的过度简化示例,即:

using delegate = variant<
    function<void()>,
    function<void(const string&)>,
    function<void(const optional<string>&)>
>;

struct foo
{
    void add_delegate(delegate fn) { fns.push_back(std::move(fn)); }
    vector<delegate> fns;
};

int main()
{
    foo bar;
    bar.add_delegate([&](){ });
    bar.add_delegate([&](const string&){ });
    bar.add_delegate([&](const optional<string>&){ }); // ERROR
    return 0;
}

最后一次调用 add_delegate无法编译,因为它无法在 function<void(const string&)> 之间做出决定和 function<void(const optional<string>&)> .

我的理解是这个问题与重载决议有关(因此我的原始示例)。我应该对 add_delegate 做哪些更改?允许它接受所有 3 个版本的 lambda 表达式?

完整示例可以在 Coliru 上找到.

最佳答案

lambda 不是 std::function<> . std::function<R(Args...)>是一种类型删除值类型,可以存储与 R(Args...) 调用兼容的任何可复制对象.

在上面的一个例子中,Rvoid (对于 std::function 表示“我不在乎它返回什么”),而 Args...std::string 。如果您可以使用 std::string 右值调用可调用对象,则可调用对象与此调用兼容。

std::optional<std::string> 都是如此和 std::string .

“完全匹配”没有特殊的重载——重要的是调用兼容与否。

有几种方法可以解决这个问题。

template<std::size_t N>
struct overload_order : overload_order<N-1> {};
template<>
struct overload_order<0> {};

namespace details {
  void foo(overload_order<1>, fn_string) { cout << "string" << endl; }
  void foo(overload_order<0>, fn_optional_string) { cout << "optional string" << endl; }
}
template<class F>
void foo(F&& f) {
  foo( overload_order<!std::is_same<std::decay_t<F>, fn_optional_string>{}>{}, std::forward<F>(f) );
}

现在我们首先尝试 fn_string一个,只有当失败时,我们才会尝试 fn_optional_string , 除非参数已经是 fn_optional_string ,在这种情况下,我们直接分派(dispatch)到该重载。

关于c++ - 使用带有 std::optional 参数的 std::function 重载歧义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49567521/

相关文章:

c++ - 不安全, `noexcept` 和访问 `std::variant` 的无开销方式

c++ - 什么是关于国际化和本地化的良好介绍和教程?

c++ - 如何在mac中编辑/usr/include/dlfcn.h

c++ - 关于编译程序如何与操作系统交互的问题

c++ - 不同结构的列表( vector )

c++ - 在 boost-variant 中调用匹配类型的析构函数

c++ - 如何使用 C++14 和 C++1z 中的功能缩短此可变参数模板代码?

C++:std::invoke 是否需要完美转发?

C++17 支持 Eclipse Neon

c++ - 为什么非平凡成员需要在同一类中为匿名 union 定义构造函数