【问题标题】:Overload resolution with variadic higher order functions具有可变高阶函数的重载分辨率
【发布时间】:2014-05-06 16:46:18
【问题描述】:

假设我有一个可变高阶函数

template<typename F, typename ...Args>
void execution(F func, Args&&... args)
{
    func(std::forward<Args>(args)...);
}

那么对于这个重载集

void f() {}
void f(int arg) {}

重载分辨率为impossible

int main()
{
    execution(f, 1);
    execution(f);
    return 0;
}

然而,如果只提供两者之一,程序compiles

  1. 为什么会发生这种情况?为什么模板参数推导失败?
  2. 如果我从集合中删除 f() 并将其替换为 f(arg, arg2),则仍然是 problem。是否有解决方法,或者 我是否总是必须将函数的 provide the type 作为模板参数?

    execution<void()>(f);
    

【问题讨论】:

  • 有 C++ Jon Skeet 吗?
  • 这首先是模板参数推导的问题,而不是重载解析的问题。
  • C++1y的解决方案是写execution([](auto&amp;&amp;... pp){ return f(std::forward&lt;decltype(pp)&gt;(pp)...); });。这是一个已知问题,我猜这个问题有多个重复项。

标签: c++ templates c++11 variadic-templates


【解决方案1】:

在做模板类型推导时,编译器不会分析函数内部是如何使用类型的。因此,在推导模板参数时,编译器看不到不同模板参数之间的关系。因此,额外的可变参数模板参数根本不重要,问题可以简化为

template<typename Func> void execution(Func func);

void f();
void f(int);

execution(f);

有了这个最小化的代码,模板参数推导失败的原因应该很明显了。

您可以通过使您的第一个参数显式依赖于其余模板参数来解决此问题,例如:

template<typename... Args>
 void execution(void (*func)(Args ...), Args ... args)
{
  func(std::forward<Args>(args) ...);
}

【讨论】:

  • std::function 不需要 SFINAE 拒绝无法为模板参数中提供的参数类型调用的函数。这是一个 QoI 问题。
  • 此外,您的解决方案需要在调用站点将f 显式转换为std::function 类型:不同函数之间的重载解决方案仅在给出显式目标类型时才会发生(例如void execution( void(*)(int) ); )。 std::function 类型只有构造函数模板,因此不会发生重载决议,f 会模棱两可。
  • @dyp:谢谢,我不知道std::function 的问题。我已将代码更改为简单的函数指针;虽然不太灵活,但它应该可以解决手头的问题。
  • void foo(int); template&lt;typename Args ...&gt; void execution(void (*func)(Args ...), Args ... args); int main(){ execution( foo, 3.0 ); } 做什么?即,这个修复是不够的。
  • @Yakk:它给出了一个错误,因为没有f(double)。我会考虑预期的行为。您期望哪种行为?
【解决方案2】:

您需要一个重载集对象。这是一个代表f的整个重载函数集的对象:

struct f_overload_set {
  template<typename...As>
  auto operator()(As&&...as)->
    decltype(f(std::declval<As>()...))
  { return f(std::forward<As>(as)...); }
};

现在将f_overload_set{} 传递给您的template 函数。

templates 函数值参数必须是值,并且在当前的 C++ 中没有代表函数的整个重载集的第一类值。符号f 在每个使用点被消除歧义为一个重载:但这需要在使用点的直接上下文。上面的内容推迟了消歧,直到我们手头有论据。

【讨论】:

  • 这样就解决了。我以为我已经弄明白了creating overload sets explicitly
  • C++14:#define LIFT(f) [](auto&amp;&amp;... a) -&gt; decltype(auto){ return f(std::forward&lt;decltype(a)&gt;(a)...); }。如果您不想依赖 &lt;utility&gt;,请将 std::forward 替换为 static_cast
【解决方案3】:

没有特定的函数'f'可用:

简化它:

template<typename F>
void execution(F func)
{}

void f() {}
void f(int arg) {}

int main()
{
    execution(f);
    return 0;
}

您有两个函数 'f' 可用于模板参数扣除/替换

【讨论】:

    猜你喜欢
    • 2012-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 2016-09-10
    • 2011-02-11
    • 2013-09-12
    • 1970-01-01
    相关资源
    最近更新 更多