【问题标题】:Compiler fails to deduce types when using std::function编译器在使用 std::function 时无法推断类型
【发布时间】:2017-04-25 19:11:30
【问题描述】:

所以,我有一个类的方法及其重载,如下所示:

bool my_for_each(someIterator begin, someIterator end, bool (*_lambda)(someIterator));

void my_for_each(someIterator begin, someIterator end, void (*_lambda)(someIterator));

如您所见,唯一的区别是传递函数的签名,即它的返回类型。 虽然上面的代码在我调用它时工作得很好:

my_for_each(iteratorBegin, iteratorEnd, [](someIterator) {; }); // calls void version

my_for_each(iteratorBegin, iteratorEnd, [](someIterator)->bool {return false; }); // calls bool version

...,如果我将 my_for_each 函数编写如下:

bool my_for_each(someIterator begin, someIterator end, std::function<bool(someIterator)> _lambda);

void my_for_each(someIterator begin, someIterator end, std::function<void(someIterator)> _lambda);

如果我以相同的方式调用函数(C2668 对重载函数的模糊调用),则代码无法编译。虽然,如果我手动转换函数:

my_for_each(iteratorBegin, iteratorEnd, static_cast<std::function<void(someIterator)>>([](someIterator) {; })); //calls void version

my_for_each(iteratorBegin, iteratorEnd, static_cast<std::function<bool(someIterator)>>([](someIterator) -> bool { return false; })); //calls bool version

代码运行良好。所以我只是想知道:

  1. 为什么普通函数指针类型推导比std模板“强”?
  2. 是否有某种解决方法可以在不手动转换参数的情况下仍使用更通用的版本?

编译器是VS2015。

谢谢,祝你有美好的一天!

【问题讨论】:

  • @FrançoisAndrieux 已修复,只是一个转录错误
  • my_for_each函数的返回值是什么意思?
  • 它似乎是两个迭代器形成一个范围,以及一个接受迭代器并对其执行某些操作的函数。我不确定第二个版本中的所有bools 都在做什么(可能是 and 或 an or 操作)。
  • @FrançoisAndrieux 如果每个 lambda 调用都返回 true,则返回 true,否则返回 false。

标签: c++ overloading type-deduction


【解决方案1】:

std::function 和 lambda 具有不同的类型,这意味着第二个示例需要从 lambda 的类型隐式转换为 std::function

问题是返回 bool 的 lambda 可以隐式转换为 std::function&lt;bool(T)&gt;std::function&lt;void(T)&gt;,因此这两个重载对于您的调用都是同样有效的选择,从而导致歧义错误。当您手动将它们转换为正确的 std::function 时,歧义得到明确解决。

编辑:解决方法

您可以通过提供额外的模板化重载来解决此问题,该重载接受任何类型的可调用类型,推断该可调用类型的返回类型并自动执行强制转换。为了通用性,我已将 someIterator 更改为模板参数。由于您没有提供 my_for_each 函数的实现细节,我省略了这些实现。由于您似乎只想支持voidbool 返回类型,因此我添加了static_assert 以在提供不支持的返回类型时生成明确的编译器错误。

#include <functional>
#include <type_traits>

// Implement for std::function<bool(iter)>
template<class I>
bool my_for_each(I begin, I end, std::function<bool(I)> lambda);

// Implement for std::function<void(iter)>
template<class I>
void my_for_each(I begin, I end, std::function<void(I)> lambda);

// Dispatch to the right overload
template<class T, class I>
auto my_for_each(I begin, I end, T&& lambda)
{
    using return_type = decltype(lambda(begin));    // Obtain the return type of lambda
    static_assert(std::is_same<return_type, bool>::value || std::is_same<return_type, void>::value,
        "my_for_each only accepts function objects that return void or bool");
    using function_type = std::function<return_type(I)>;    // The type to cast lambda to
    return my_for_each(begin, end, function_type(std::forward<T>(lambda))); // Preform the cast
}

【讨论】:

  • 修复会改善这个答案。
  • 所以,如果我想使用我自己的可调用对象,我必须每次都进行静态转换,对吗?
  • @Alfaix 我已经编辑了答案并进行了修复,以避免每次都手动转换。
  • @Yakk 谢谢你的建议。
【解决方案2】:

原因是非捕获 lambdas 只能转换为函数指针,它可以接受使用类型的参数。

另一方面,std::function 有一个模板化的构造函数,它接受所有内容。因此std::function&lt;bool...&gt;std::function&lt;void ...&gt; 都可以从您的lambda 中创建,以实现my_for_each 的重载分辨率。 (请注意,虽然无法创建这些对象)。

【讨论】:

    猜你喜欢
    • 2018-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    相关资源
    最近更新 更多