请考虑以下示例:
template <typename T>
void f (T t) {
std::cout << t << std::endl;
}
template <typename T>
struct F {
static constexpr void (*m) (T) = &f;
};
和用法:
F<int>::m (10);
到目前为止,这么好。当我想存储指向函数模板的指针时出现问题,该函数模板采用例如lambda表达式。考虑一下这个:
template <typename T, typename C>
void g (T t, C c) {
std::cout << c (t) << std::endl;
}
template <typename T, typename C>
struct G {
static constexpr void (*m) (T, C) = &g;
};
和用法:
auto l = [] (auto v) { return v; };
G<int, decltype (l)>::m (20, l);
在GCC 5.3.1上编译:
g++-5 -std=c++14 -Wall -Wextra -Wpedantic -Werror=return-type main.cpp -o main
我得到了:
‘constexpr void (* const G<int, main(int, char**)::<lambda(auto:1)> >::m)(int, main(int, char**)::<lambda(auto:1)>)’, declared using local type ‘main(int, char**)::<lambda(auto:1)>’, is used but never defined [-fpermissive]
为什么会这样?
有什么方法可以让我的代码工作吗?
我不感兴趣的一种可能的解决方案:
struct O {
template <typename T>
T operator() (T v) {
return v;
}
};
用法:
G<int, O>::m (20, O {});
答案 0 :(得分:4)
错误,如果你删除一些东西,说:
[...]
m
[...]被使用但从未定义[-fpermissive]
所以只需按照编译器的命令进行定义:
template <class T, class C>
constexpr void (*G<T,C>::m)(T,C);
现在它有效。这是因为在C ++ 14中,您需要从[class.static.data]添加静态constexpr数据成员的定义:
如果程序中使用了odr-used([basic.def.odr]),并且命名空间范围定义不包含初始值设定项,那么[静态数据]成员仍应在命名空间作用域中定义。
由p0386导致constexpr
静态数据成员在C ++ 17中不再需要这样做。文字now reads:
如果使用
constexpr
说明符声明[static data]成员,则可能在名称空间作用域中重新声明而没有初始化程序(不推荐使用此用法;请参阅[depr.static_constexpr] )。