【问题标题】:Template argument deduction failed, using function pointers as argument模板参数推导失败,使用函数指针作为参数
【发布时间】:2016-07-11 17:05:41
【问题描述】:

我编写了许多模板函数。示例:

template <typename T>
inline int Foo(T* t, unsigned char** p, size_t* size, size_t (*MyHandler)(T*))
{
    ...
}

template <>
inline int Foo<Bar>(Bar* t, unsigned char** p, size_t* size, size_t (*MyHandler)(Bar*))
{
    ...
}

如果我使用显式模板参数调用这些函数,那么一切正常,即

Foo<Bar>(&myBar, pBuffer, size, FooFoo<Bar>);

FooFoo&lt;T&gt; 是一个不同的模板函数

如果用户不需要跟踪类型,那将是非常有利的。所以我想使用以下语法:

Foo(&myBar, pBuffer, size, FooFoo);

但是,这条线却造成了一对

  • C2784:无法从 'Bar *' 推导出 'T *' 的模板参数
  • C2796:不能使用函数模板 'size_t FooFoo(T *) 作为函数参数

与Foo 一样,存在一个专门化FooFoo&lt;Bar&gt;

据我所知,应该可以推导出 T,因为第一个参数的类型为 T* - 或者该函数指针是否与推导混淆?

任何提示将不胜感激。

更新 3 根据@Yakk 建议的更改请求的 MCVS 最终有效!

#include <stdio.h>

struct Bar
{
    int a;
    int b;
    int c;
};


template<class T>struct id_t{typedef T type;}; 

template <typename T>
inline size_t FooFoo(T* t)
{
    printf("FooFoo calculating size of T...\r\n", sizeof(T));
    return sizeof(T);
}

template <>
inline size_t FooFoo<Bar>(Bar* t)
{
    printf("FooFoo calculating size of Bar...\r\n", sizeof(Bar));
    return sizeof(Bar);
}

template <typename T>
inline int Foo(T* t, unsigned char** p, size_t* size, typename id_t<size_t (*)(T*)>::type MyHandler)
{
    printf("Foo telling size of T: %u\r\n", MyHandler(t));
    return 0;
}

template <>
inline int Foo<Bar>(Bar* t, unsigned char** p, size_t* size, typename id_t<size_t (*)(Bar*)>::type MyHandler)
{
    printf("Foo telling size of Bar: %u\r\n", MyHandler(t));
    return 0;
}



int main(int argc, char* argv[])
{
    int i;
    Bar myBar;

    Foo<Bar>(&myBar, nullptr, 0, FooFoo<Bar>); //  works
    Foo<int>(&i, nullptr, 0, FooFoo<int>); // works

    Foo(&myBar, nullptr, 0, FooFoo<Bar>); //  works
    Foo(&i, nullptr, 0, FooFoo<int>); // works


    Foo(&myBar, nullptr, 0, FooFoo);    // error C2896: 'int Foo(T *,unsigned char **,size_t *,size_t (__cdecl *)(T *))' : cannot use function template 'size_t FooFoo(T *)' as a function argument
                                        // error C2784: 'int Foo(T *,unsigned char **,size_t *,size_t (__cdecl *)(T *))' : could not deduce template argument for 'T *' from 'Bar *'

    Foo(&i, nullptr, 0, FooFoo);        // error C2896: 'int Foo(T *,unsigned char **,size_t *,size_t (__cdecl *)(T *))' : cannot use function template 'size_t FooFoo(T *)' as a function argument
                                        //error C2784: 'int Foo(T *,unsigned char **,size_t *,size_t (__cdecl *)(T *))' : could not deduce template argument for 'T *' from 'int *'

    return 0;
}

非常感谢!

【问题讨论】:

  • 只是好奇,但你试过Foo(&amp;myBar, pBuffer, size, FooFoo&lt;Bar&gt;);吗?
  • 无法使用 g++ 5.1 (TDM)、clang 或 VC2015 重现 - 请在您的帖子中添加 Minimum, Complete and Verifiable example 和有关您的编译器的详细信息,这样我们就不必猜测出了什么问题。
  • @Holt 我已经更新了详细信息,编译器是 MSVC 2012
  • @antipattern 感谢您的更新,您的 MCVE 有一个小错误(我猜第一次返回应该是 sizeof(T) 而不是 T)。我没有 MSVC2012,但我确认您提供的示例在 MSVC2015 上编译并运行良好,不知道它是否符合标准……
  • @Holt 感谢您的测试,这是一件好事,因为我这里只有这个旧版本,没有别的。我担心这毕竟是不可能的。

标签: c++ templates visual-studio-2012


【解决方案1】:

所有参数在推导时具有相同的权重,并且是独立推导的。要阻止使用特定参数进行演绎,请执行以下操作:

template<class T>struct id_t{typedef T type;};
template<class T>using block_deduction=typename id_t<T>::type;

现在拿size_t (*MyHandler)(T*) 做吧:

block_deduction<size_t (*)(T*)> MyHandler

或者在非 C++11 编译器中:

typename id_t<size_t (*)(T*)>::type MyHandler

这样做是将MyHandler 参数的类型放入非推导上下文中。因此,编译器不会尝试,而是使用第一个参数的推导来确定T。然后生成MyHandler 的类型。然后根据签名解决您传递的重载集,并且一切正常(可能)。

【讨论】:

  • 感谢您添加解释。在专用 Foo 的情况下还是仅在声明通用模板化 Foo 时,我是否也必须使用 block_deduction?因为当我在专用 Foo 的声明中使用 Intellisense 时显示错误(“没有函数声明的实例与指定的类型匹配”)。
  • 显然我的 Visual Studio 版本不支持这个。太糟糕了:/有什么办法可以摆脱using关键字?
  • @antipattern 使用typename id_t&lt;size_t (*)(T*)&gt;::type MyHandler,而不是Yakk最后提出的。
  • @Holt 第一行已经出现错误:error C2873: 'type' : symbol cannot be used in a using-declaration。我在这里 stackoverflow.com/questions/23374003/… 发现了一个模糊相似的问题,但无法让它对我有用。
  • @antipattern 您需要完全删除using 声明并使用我建议的内容而不是block_deduction&lt;size_t (*)(T*)&gt; MyHandler。
【解决方案2】:

您的问题不是T=Bar 对Foo 的扣除——而是FooFoo 的扣除。

【讨论】:

  • 好吧,我也确实有过这种怀疑。另一方面,如果我将 Foo 悬停在第二次调用中,那么 Intellisense 确实会显示正确推断的签名:Foo&lt;Bar&gt;(Bar* t, unsigned char pBuffer, size_t size, size_t (*MyHandler)(Bar*))。
猜你喜欢
  • 1970-01-01
  • 2018-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多