当前位置: 首页 > 知识库问答 >
问题:

使用std::函数的模板类型推导

壤驷鸿
2023-03-14

我发现了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中编译时。

为什么类型演绎不从函数类型中演绎模板类型,有没有变通方法?

共有2个答案

雍骏俊
2023-03-14

在模板参数推导期间不执行隐式转换,除了:temp.deduct.call

通常,演绎过程会尝试查找模板参数值,这些值将使推导的 A 与 A 相同(在如上所述转换类型 A 之后)。但是,有三种情况允许差异:

  • 如果原始P是引用类型,推导出的A(即引用所指的类型)可以比转换后的A更符合cv条件。
  • 转换后的A可以是另一个指针或指向成员类型的指针,可以通过函数指针转换([conv.fctptr])和/或限定转换([conv.qual])转换为推导的A。
  • 如果P是一个类,并且P的形式为simple-temping-id,那么转换后的A可以是推导出的A的派生类。同样,如果P是指向Simple-temping-id形式的类的指针,则转换后的A可以是指向推导出的A指向的派生类的指针。

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

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

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

stdfunc_test<int>([](int _) {return _ + 2;});
stdfunc_test<int>(test_func);
裴星洲
2023-03-14

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

#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

 类似资料:
  • 多亏了C11,我们收到了系列的仿函数包装器。不幸的是,我一直只听到关于这些新添加的不好的消息。最受欢迎的是它们非常慢。我测试了它,与模板相比,它们真的很糟糕。 111毫秒对1241毫秒。我认为这是因为模板可以很好地内联,而通过虚拟调用覆盖内部。 显然,在我看来,模板也有其问题: 它们必须以头的形式提供,这不是您在以封闭代码形式发布库时可能不希望做的事情, 因此,我可以假设s可以用作传递函子的事实标

  • 如果我没有理解错的话,类模板定义了一个函数,所以当我调用时,编译器有可能进行隐式强制转换,但是在函数模板的情况下,此时没有函数定义,所以隐式强制转换不会发生。 但我不明白为什么编译器不能创建函数定义,然后应用隐式强制转换? 错误是: 在函数“int main()”中:    25:24:错误:调用“test2::add(void(&)(int))”没有匹配函数    25:24:注:候选人是:  

  • 使用以下代码生成时 生成以下诊断(代码后): 诊断: 有趣的部分不是关于歧义错误本身(这不是这里主要关注的问题)。有趣的是,当仅使用函数名调用fun时,第一个fun的模板参数F被解析为纯函数类型double(double),而第二个fun的模板参数F被解析为更期望的函数指针类型。 然而,当我们将调用<代码>乐趣(测试) 更改为<代码>乐趣( 这种行为似乎是所有Clang和GCC(以及Visual

  • 我试图编写一个通用的obj工厂,使用可变模板调用各种类的构造函数。代码如下: 在大多数例子中,变量arg总是这样写“Args” 错误:没有可行的转换从'__bind( 在移除“ 但我不知道为什么?

  • 我有一个模板类和一个模板返回类型的函数: 假设我想为一个特定类型扩展返回的,并专门为该类型使用函数。因此,让我们创建一个模板来保存返回类型,并在中使用它: 并用它来提供专业化: 然而,gcc和clang都拒绝了最终声明。例如,clang返回: 错误:没有与函数模板专门化“build_wrapped”匹配的函数模板 注意:已忽略候选模板:无法推断模板参数“t” 通过为创建一个明确的专门化,我可以使用