【问题标题】:Template type deduction with std::function用 std::function 推导模板类型
【发布时间】:2016-08-26 19:30:39
【问题描述】:

我发现std::function 和类型推导有以下行为,这对我来说是出乎意料的:

#include <functional>

template <typename T>
void stdfunc_test(std::function<T(T)> func) {};

int test_func(int arg)
{
    return arg + 2;
}

int main()
{
    stdfunc_test([](int _) {return _ + 2;});
    stdfunc_test(test_func);
}

main 中的两行都导致错误:

没有函数模板“stdfunc_test”的实例与参数列表匹配

尝试在 Visual Studio 2015 中编译时。

为什么类型推导不从函数类型中扣除模板类型,有没有解决办法?

【问题讨论】:

  • lambda 和函数指针实际上都不是 std::function - 它们可以隐式转换为 std::function
  • 在推导模板变量时,是不是不考虑这样的隐式转换?
  • 假设它是一个函数:template &lt;typename T&gt; void stdfunc_test(T func)。然后用 SFINAE 限制模板的范围。
  • 否,模板参数推导不考虑自定义转换。即使它做到了(虽然它没有) - std::function 有一堆模板化的构造函数,它们接受几乎任何东西,给予或接受。

标签: c++ templates c++11 std-function


【解决方案1】:

您可以使用模板来推断函数和函子的签名:

#include<functional>

template<class T>
struct AsFunction
    : public AsFunction<decltype(&T::operator())>
{};

template<class ReturnType, class... Args>
struct AsFunction<ReturnType(Args...)> {
  using type = std::function<ReturnType(Args...)>;
};

template<class ReturnType, class... Args>
struct AsFunction<ReturnType(*)(Args...)> {
  using type = std::function<ReturnType(Args...)>;
};


template<class Class, class ReturnType, class... Args>
struct AsFunction<ReturnType(Class::*)(Args...) const> {
  using type = std::function<ReturnType(Args...)>;
};

template<class F>
auto toFunction( F f ) -> typename AsFunction<F>::type {
  return {f};
}

template <typename T>
void stdfunc_test(std::function<T(T)> func) {};

int test_func(int arg)
{
    return arg + 2;
}


int main()
{

    stdfunc_test( toFunction([](int _) {return _ + 2;}) );
    stdfunc_test( toFunction(test_func) );
    return 0;
}

您可以在这里现场试用:http://fiddle.jyt.io/github/d4ab355eb2ab7fc4cc0a48da261f0127

【讨论】:

    【解决方案2】:

    模板参数推导过程中不进行隐式转换,除了:temp.deduct.call

    一般来说,推导过程试图找到模板参数值,使推导的 A 与 A 相同(在类型 A 如上所述转换之后)。但是,有三种情况允许区别:

    • 如果原始 P 是引用类型,则推导的 A(即引用所引用的类型)可能比转换后的 A 更具 cv 限定性。
    • 转换后的 A 可以是另一个指针或指向成员类型的指针,可以通过函数指针转换 ([conv.fctptr]) 和/或限定转换 ([conv.qual]) 将其转换为推导的 A。李>
    • 如果 P 是一个类并且 P 具有 simple-template-id 形式,则转换后的 A 可以是推导的 A 的派生类。同样,如果 P 是一个指向 simple-template-形式的类的指针id,转换后的 A 可以是指向派生类的指针,该派生类由推导的 A 指向。

    但是,如果模板参数不参与模板参数推导,则会进行隐式转换:(temp.arg.explicit)

    如果参数类型不包含参与模板参数推导的模板参数,将对函数参数执行隐式转换(子句 [conv])以将其转换为相应函数参数的类型。 [注:如果显式指定模板参数,则不参与模板参数推导。

    因此,如果您明确指定模板参数,它应该可以工作:

    stdfunc_test<int>([](int _) {return _ + 2;});
    stdfunc_test<int>(test_func);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多