是否可以显式专门化模板来匹配lambda?

时间:2016-06-16 03:08:34

标签: c++ templates c++11 gcc lambda

假设我有一个标题wrapper.h

template <typename Func> void wrapper(const Func func);

和包含以下内容的文件wrapper.cpp

#include "wrapper.h"
template <typename Func>
void wrapper(const Func  func)
{
  func();
}

包含以下内容的文件main.cpp

#include "wrapper.h"
#include <iostream>

int main()
{
  wrapper( [](){std::cout<<"hello."<<std::endl;} );
}

如果我将这些编译在一起(例如cat wrapper.cpp main.cpp | g++ -std=c++11 -o main -x c++ -),我就不会收到链接器错误。

但是如果我单独编译它们(例如,g++ -std=c++11 -o wrapper.o -c wrapper.cpp && g++ -std=c++11 -o main main.cpp wrapper.o),我当然---得到一个链接器错误:

Undefined symbols for architecture x86_64:
  "void wrapper<main::$_0>(main::$_0)", referenced from:
      _main in main-5f3a90.o

通常情况下,我可以明确专门化wrapper并将此类内容添加到wrapper.cpp

template void wrapper<void(*)()>(void(*)())

但是这种特殊的模板专业化不起作用。

是否可以在lambda上专门化模板?

3 个答案:

答案 0 :(得分:9)

首先,我假设您了解Why can templates only be implemented in the header file?

问题:

  

是否可以在lambda上专门化模板?

不幸的是,不,模板专精可以使用完全匹配,而lambda是一种唯一的未命名类型。问题是专门针对那种你不知道的类型。

最好的办法是使用std::function;或者你已经完成了,然后通过添加+

强制将lambda转换为函数指针
int main()
{
  wrapper(+[](){std::cout<<"hello."<<std::endl;} );
}

完整示例:

#include <iostream>

template <typename Func>
void wrapper(const Func  func)
{
    std::cout << "PRIMARY\n";
    func();
}

template <>
void wrapper<void(*)()>(void(*func)())
{
    std::cout << "SPECIALIZATION\n";
    func();
}

int main()
{
     wrapper([](){std::cout<<"hello\n"<<std::endl;} );
     wrapper(+[](){std::cout<<"world."<<std::endl;} );
}

这将打印

PRIMARY
hello

SPECIALIZATION
world

此外,decltype设施无济于事,if it does,它会带走您对lambda的需求的灵活性

答案 1 :(得分:0)

不幸的是,你不能。

您的问题是lambda类型是在每个编译单元内随机生成的。

您可以跨单位使用函数,因为您可以在标题中声明它们;然后链接器将在定义它的编译单元中找到具有正确名称和类型的链接器。如果声明变量,则相同,但不常见。如果声明导致每个单元中的类型不同,则会失败,并且不可能跨单元进行交互。

所以,如果你试图让那个lambda成为&#34;相同的&#34;两个单元中的对象(即在标题中定义)链接将失败,因为您无法两次定义同一对象。如果你把它们变成了不同的&#34;对象(即添加内联,或在源中定义)它们将具有不同的类型,因此链接将无法按照您的需要统一它们。无法获胜。

答案 2 :(得分:0)

听取:Why can templates only be implemented in the header file?

因此,当模板定义从文件wrapper.cpp移动到headerfile wrapper.h时,可以通过main.cpp中建议的方式调用wrapper():

int main()
{
  wrapper( [](){std::cout<<"hello"<<std::endl;} );
  wrapper(+[](){std::cout<<"world"<<std::endl;} );
  wrapper(std::function<void()>( [](){std::cout<<"best"<<std::endl;} ));
}