分别为数据成员和成员函数特化模板
Specialize template separately for data member and member functions
我想专门化一个模板来对指向数据成员的指针做一件事,对指向成员函数的指针做另一件事。这在 gcc 11 之前一直有效,成员函数的作用更为具体。它仍然适用于 clang 11,但似乎与 gcc 不兼容。
这是一个最小的非工作示例:
#include <iostream>
template<auto F> struct which;
template<typename K, typename V, K V::*F>
struct which<F> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V, K (V::*F)()>
struct which<F> {
static constexpr char desc[] = "pointer to member function";
};
struct S {
int i;
int f() { return 0; }
};
int
main()
{
std::cout << "S::i: " << which<&S::i>::desc << std::endl;
std::cout << "S::f: " << which<&S::f>::desc << std::endl;
}
从 gcc 11.1 开始,使用 g++ -std=c++17 memptr.cc
编译会得到:
memptr.cc: In function 'int main()':
memptr.cc:24:40: error: ambiguous template instantiation for 'struct which<&S::f>'
24 | std::cout << "S::f: " << which<&S::f>::desc << std::endl;
| ^~
memptr.cc:6:8: note: candidates are: 'template<class K, class V, K V::* F> struct which<F> [with K = int(); V = S; K V::* F = &S::f]'
6 | struct which<F> {
| ^~~~~~~~
memptr.cc:11:8: note: 'template<class K, class V, K (V::* F)()> struct which<F> [with K = int; V = S; K (V::* F)() = &S::f]'
11 | struct which<F> {
| ^~~~~~~~
memptr.cc:24:42: error: incomplete type 'which<&S::f>' used in nested name specifier
24 | std::cout << "S::f: " << which<&S::f>::desc << std::endl;
| ^~~~
这是 gcc 中的错误,还是我的代码中的错误?无论哪种方式,最简单的解决方法是什么?
我不确定,但我怀疑这是一个 GCC 错误。作为一种变通方法,您可以通过在专业化的 参数列表 而不是 参数列表中写入 F
的签名来稍微修改您的代码,并推导一个类型而不是非类型
template<typename F> struct which;
template<typename K, typename V>
struct which<K V::*> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V>
struct which<K (V::*)()> {
static constexpr char desc[] = "pointer to member function";
};
要使用它,你需要写 which<decltype(&S::i)>::desc
因为需要一个类型。
如果您希望将实际指针而不是类型传递给特化,您也可以执行以下操作,让现有类型特征完成工作
// implementation
template<auto, bool> struct which_impl;
template<auto F>
struct which_impl<F, true> {
static constexpr char desc[] = "pointer to data member";
};
template<auto F>
struct which_impl<F, false> {
static constexpr char desc[] = "pointer to member function";
};
// helper alias
template<auto F>
using which = which_impl<F, std::is_member_object_pointer_v<decltype(F)>>;
在 C++20 中,您可以使用 concepts 通过定义这样的概念来确保编译器不匹配 K = int()
之类的东西(就像 GCC 那样)作为
template <class T>
concept no_function = !std::is_function_v<T>;
然后为模板参数强制执行它 K
template<no_function K, typename V, K V::*F>
struct which<F> {
static constexpr char desc[] = "pointer to data member";
};
template<no_function K, typename V, K (V::*F)()>
struct which<F> {
static constexpr char desc[] = "pointer to member function";
};
使用@cigien 的答案加上默认模板参数,以下似乎以向后兼容的方式解决了可能是 gcc 错误的问题:
template<auto F,
bool = std::is_member_function_pointer_v<decltype(F)>> struct which;
template<typename K, typename V, K V::*F>
struct which<F, false> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V, K (V::*F)()>
struct which<F, true> {
static constexpr char desc[] = "pointer to member function";
};
我想专门化一个模板来对指向数据成员的指针做一件事,对指向成员函数的指针做另一件事。这在 gcc 11 之前一直有效,成员函数的作用更为具体。它仍然适用于 clang 11,但似乎与 gcc 不兼容。
这是一个最小的非工作示例:
#include <iostream>
template<auto F> struct which;
template<typename K, typename V, K V::*F>
struct which<F> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V, K (V::*F)()>
struct which<F> {
static constexpr char desc[] = "pointer to member function";
};
struct S {
int i;
int f() { return 0; }
};
int
main()
{
std::cout << "S::i: " << which<&S::i>::desc << std::endl;
std::cout << "S::f: " << which<&S::f>::desc << std::endl;
}
从 gcc 11.1 开始,使用 g++ -std=c++17 memptr.cc
编译会得到:
memptr.cc: In function 'int main()':
memptr.cc:24:40: error: ambiguous template instantiation for 'struct which<&S::f>'
24 | std::cout << "S::f: " << which<&S::f>::desc << std::endl;
| ^~
memptr.cc:6:8: note: candidates are: 'template<class K, class V, K V::* F> struct which<F> [with K = int(); V = S; K V::* F = &S::f]'
6 | struct which<F> {
| ^~~~~~~~
memptr.cc:11:8: note: 'template<class K, class V, K (V::* F)()> struct which<F> [with K = int; V = S; K (V::* F)() = &S::f]'
11 | struct which<F> {
| ^~~~~~~~
memptr.cc:24:42: error: incomplete type 'which<&S::f>' used in nested name specifier
24 | std::cout << "S::f: " << which<&S::f>::desc << std::endl;
| ^~~~
这是 gcc 中的错误,还是我的代码中的错误?无论哪种方式,最简单的解决方法是什么?
我不确定,但我怀疑这是一个 GCC 错误。作为一种变通方法,您可以通过在专业化的 参数列表 而不是 参数列表中写入 F
的签名来稍微修改您的代码,并推导一个类型而不是非类型
template<typename F> struct which;
template<typename K, typename V>
struct which<K V::*> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V>
struct which<K (V::*)()> {
static constexpr char desc[] = "pointer to member function";
};
要使用它,你需要写 which<decltype(&S::i)>::desc
因为需要一个类型。
如果您希望将实际指针而不是类型传递给特化,您也可以执行以下操作,让现有类型特征完成工作
// implementation
template<auto, bool> struct which_impl;
template<auto F>
struct which_impl<F, true> {
static constexpr char desc[] = "pointer to data member";
};
template<auto F>
struct which_impl<F, false> {
static constexpr char desc[] = "pointer to member function";
};
// helper alias
template<auto F>
using which = which_impl<F, std::is_member_object_pointer_v<decltype(F)>>;
在 C++20 中,您可以使用 concepts 通过定义这样的概念来确保编译器不匹配 K = int()
之类的东西(就像 GCC 那样)作为
template <class T>
concept no_function = !std::is_function_v<T>;
然后为模板参数强制执行它 K
template<no_function K, typename V, K V::*F>
struct which<F> {
static constexpr char desc[] = "pointer to data member";
};
template<no_function K, typename V, K (V::*F)()>
struct which<F> {
static constexpr char desc[] = "pointer to member function";
};
使用@cigien 的答案加上默认模板参数,以下似乎以向后兼容的方式解决了可能是 gcc 错误的问题:
template<auto F,
bool = std::is_member_function_pointer_v<decltype(F)>> struct which;
template<typename K, typename V, K V::*F>
struct which<F, false> {
static constexpr char desc[] = "pointer to data member";
};
template<typename K, typename V, K (V::*F)()>
struct which<F, true> {
static constexpr char desc[] = "pointer to member function";
};