【问题标题】:overloaded function resolution with std::function: helping compilers disambiguate使用 std::function 重载函数解析:帮助编译器消除歧义
【发布时间】:2015-07-15 11:27:31
【问题描述】:

我想从一个重载的模板函数创建一个std::function。使用 g++ -std=c++14 编译我得到一个重载解析错误。我有一个技巧可以将函数模板按摩成编译器可以识别的形式,但我想知道是否有更优雅的方法。下面是说明错误和我的 hack 的代码,

#include <functional>

template <typename T>
T foo(T t) { return t; }

template <typename T>
T foo(T t1, T t2){ return t1 + t2; }

int main (){
    //error: conversion from ‘<unresolved overloaded function type>’ 
    //to non-scalar type ‘std::function<double(double)>’ requested
    std::function<double(double)> afunc = &foo<double>; 

    //my workaround to 'show' compiler which template 
    //function to instantiate 
    double (*kmfunc1)(double) = &foo<double>;
    std::function<double(double)> afunc = kmfunc1;  
}

我有两个问题

  • 我期望编译器解析使用哪个模板是不合理的吗?
  • 在上述情况下,创建 std::function 最优雅的方法是什么?

【问题讨论】:

  • 也可以使用[](double d){return f(d);} 代替演员表,或者通过std::function&lt;double(double)&gt; afunc = static_cast&lt;double(*)(double)&gt;(foo); 稍微更优雅的演员表(尽管那个演员表太明确了恕我直言)甚至template&lt;class Sig&gt; std::function&lt;Sig&gt; make_function(Sig* f) { return {f}; } auto afunc = make_function&lt;double(double)&gt;(foo);
  • 顺便说一句,这就是我认为std::function 应该有一个Sig* pf 构造函数的原因之一,也是为什么我的std::function-likes 有这样一个重载的原因。

标签: c++ templates c++11 overloading


【解决方案1】:
#define OVERLOAD_SET(...) \
  [](auto&&...args)-> \
    decltype(__VA_ARGS__(decltype(args)(args)...)) \
  { \
    return __VA_ARGS__(decltype(args)(args)...); \
  }

创建一个表示其参数的完整(全局)重载集的单个对象。 (我使用... 作为宏不理解现代C++ 中, 的所有用法)

std::function<double(double)> afunc = OVERLOAD_SET(foo<double>);
std::function<double(double,double)> bfunc = OVERLOAD_SET(foo<double>);

两者都应该有效

std::function<double(double)> afunc = OVERLOAD_SET(foo);
std::function<double(double,double)> bfunc = OVERLOAD_SET(foo);

当我们在做的时候。这里的想法是我们将重载决议推迟到确定参数的那一点。

OVERLOAD_SET(foo) 编译为:

[](auto&&...args)
->decltype(foo(decltype(args)(args)...))
{
  return foo(decltype(args)(args)...);
}

这是一个无状态的 lambda,它返回在其参数上调用 foo 会做的任何事情。它使用完美转发,但具有通常的缺陷。

【讨论】:

    【解决方案2】:
    • 我期望编译器解析使用哪个模板是不合理的吗?

    不允许编译器根据 C++ 语言规则来解析使用哪个模板。相关的std::function 构造函数是:

    template< class F > 
    function( F f );
    

    并且,根据[temp.deduct.type]:

    如果模板参数仅用于非推导 上下文并且没有明确指定,模板参数推导失败。

    未推断的上下文是:
    — [...]
    — 一个函数形参,因为关联的函数而无法进行实参推导 参数是一个函数,或一组重载函数 (13.4),并且适用以下一项或多项:
    多个函数匹配函数参数类型(导致二义推理), 或
    — 没有函数与函数参数类型匹配,或者
    — 作为参数提供的一组函数包含一个或多个函数模板。
    — [...]

    所以传入&amp;foo&lt;double&gt;是非推导上下文,模板推导失败,因此编译出错。

    • 在上述情况下,创建 std::function 最优雅的方法是什么?

    我只会使用演员表:

    std::function<double(double)> afunc = 
        static_cast<double(*)(double)>(&foo<double>);
    

    然后向标准委员会抱怨为什么std::function&lt;Sig&gt; 没有采用Sig* 的构造函数。以任何你想要的顺序。

    【讨论】:

    • 谢谢巴里。不过我还是有一些不明白的地方。如果确实 多个函数与函数参数类型匹配,为什么当第一个模板函数被注释掉时,代码编译失败,并出现同样的错误? (当然在第二种情况下编译应该失败,因为第二个模板函数与double(double)不兼容
    • @newling 编译错误本身具有误导性,但编译失败的事实是正确的。注意auto afunc = &amp;foo&lt;double&gt;;如果注释掉其中一个foo会编译,即模板推演成功。 template &lt;typename F&gt; function(F ) 仅在 F 可以使用正确的参数和正确的返回类型调用时才参与重载解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多