【问题标题】:Deducing function overload in a templated function在模板化函数中推断函数重载
【发布时间】:2018-10-02 21:17:07
【问题描述】:

我正在编写我自己的 std::async 模拟(必须回到 Intel13/gcc 4.4 STL),这工作正常:

template <typename Func, typename ...Args>
struct return_value {
    template <typename T>
    using decayed = typename std::decay<T>::type;
    using type    = typename std::result_of<decayed<Func>(decayed<Args>...)>::type;
};

template <typename Func, typename ...Args>
typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
    return func(args...);
}

void run(int a, double b) { 
    printf("a: %i  b: %f\n", a, b);
}

int main() {
    async(run, 1, 3.14);
}

但如果我为run 添加重载:

void run() {
    printf("no args\n");
}

那就不能正常解决:

<source>: In function 'int main()':
<source>:27:23: error: no matching function for call to 'async(<unresolved overloaded function type>, int, double)'
     async(run, 1, 3.14);
                       ^
<source>:14:43: note: candidate: 'template<class Func, class ... Args> typename return_value<Func, Args>::type async(Func&&, Args&& ...)'
 typename return_value<Func,Args...>::type async(Func &&func, Args&&... args) {
                                           ^~~~~
<source>:14:43: note:   template argument deduction/substitution failed:
<source>:27:23: note:   couldn't deduce template parameter 'Func'
     async(run, 1, 3.14);
                       ^
Compiler returned: 1

如何将函数作为模板参数并正确推断给定参数的重载?

【问题讨论】:

  • 你可以像标准一样,要求调用者选择他们想要的重载:stackoverflow.com/questions/45966603/…
  • 是的,如果可能的话,我想推断它,但似乎应该有可能以某种方式对其进行 SFINAE。
  • 您是否希望能够采用对象之类的任何函数,或者您可以将其限制为函数指针或std::function
  • 如果只是函数指针我可能会没事

标签: c++ c++11


【解决方案1】:

除非您知道返回类型,否则我个人看不到消除重载歧义的方法。您可以假设返回类型 void 最常见,然后:(为简洁起见,我正在简化您的示例)

template <class F, class... Args>
auto async(F f, Args... args)
{
    return f(args...);
}

template <class... Args>
auto async(void (*f)(Args...), Args... args)
{
    return f(args...);
}

void run();
void run(int, double);

auto test()
{
    async(run); // calls run();
    async(run, 1, 2.); // calls run(int, double);
}

这对用户来说似乎有点可疑和困惑。为什么当传递的函数返回void 时它会起作用,而如果它返回int 则不起作用?所以不推荐。

所以实际上你唯一能做的就是让用户自己去解决它。

所以你的函数调用者的一些解决方案:

好的(和丑陋的)旧方法:使用强制转换消除重载的歧义:

async(static_cast<int(*)(int, double)>(run), 1, 2.);

我个人完全不喜欢这种方法。我不喜欢它的冗长,最重要的是我不喜欢我必须明确表达一些真正应该是隐含的东西。

lambda 方式

async([] { return run(1, 2.); });

我喜欢这个。这还不错。仍然有点冗长,但比其他替代方案要好得多。

宏方式

是的,宏,在 C++ 中。事不宜迟,就是这样(为简洁起见,省略了完美的转发):

#define OVERLOAD(foo) [] (auto... args) { return foo(args...); }
async(OVERLOAD(run), 1, 2.);

我不会对此发表评论。我让你们每个人来判断这个宏。

【讨论】:

  • 我试图想出一个答案。看起来 clang++ 和 g++ 同意给定函数模板的参数 R (*)(A...),如果函数参数是非重载函数,则可以推导出 AR,并且可以推导出 AR但如果函数参数是不包含函数模板的重载集,则不能同时推导出两者。你知道这是为什么吗?
  • 注意宏方式需要 C++14 泛型 lambda,但问题标记为 c++11。
  • @aschepler 正确。 OP还有其他选择。这对其他人有用。
  • @aschepler 如果你传递了一个非重载函数,那么RArgs 都是从函数参数中推导出来的。相反,如果您传递了重载,则 RArgs 都不能从函数参数中扣除。但是Args 可以从其余的论点中扣除。
  • 不是我在测试中发现的。 coliru.stacked-crooked.com/a/706fe628dbc78012 。 (并且我希望这个示例在 [temp.deduct.call]/6 之前有效:如果 P 是函数、函数指针或指向成员函数的指针,并且参数是不包含函数模板的重载集, 尝试使用集合的每个成员进行试验论证推导。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-12
相关资源
最近更新 更多