成员到指针变量仅在带有 -std=c++17 的 Clang 中被接受为函数模板参数
member-to-pointer variable only accepted as function template argument in Clang with -std=c++17
这在 -std=c++14
的 Clang 上编译得很好
struct Vecs
{
enum class VecIndex { first, second };
std::vector<int> vec1, vec2;
template <VecIndex> struct vecForIndex;
template <> struct vecForIndex<VecIndex::first> {
static constexpr auto vecPtr = &Vecs::vec1;
};
template <> struct vecForIndex<VecIndex::second> {
static constexpr auto vecPtr = &Vecs::vec2;
};
template <std::vector<int> Vecs::*vecPtr>
static void work() {}
static void workSpecificVec() {
work<&Vecs::vec1>();
}
};
但是这个版本的 workSpecificVec 没有:
static void workSpecificVec() {
work<vecForIndex<VecIndex::first>::vecPtr>();
}
后者确实使用 -std=c++17
进行编译。
为什么?
P.S。 gcc 和 msvc 均未使用任何标准编译上述内容。 IIUC 在 class 范围 have been allowed since c++14 中的显式专业化,所以这似乎是编译器或我的理解的问题。
class 范围内的显式特化是 C++17 的一个特性 (CWG 727 was not a DR against C++14, it was new for C++17) that gcc just doesn't implement yet (this is gcc bug 85282)。
这里还有另一个 C++17 特性在发挥作用——扩大了哪些类型的参数可以用作非类型模板参数(N4198 for explanation, N4268 用于措辞)。在 C++14 中,vecForIndex<VecIndex::first>::vecPtr
不是允许的非类型模板参数,因为 只有 成员指针的允许参数(如您的示例)是:
&T::X
形式的精确表达式(就像字面上的语法)
- 计算为空指针值的任意常量表达式。
就是这样。 vecForIndex<VecIndex::first>::vecPtr
两者都不是,所以它在 C++14 中的格式不正确。在 C++17 中,对非类型模板参数的限制要宽松得多,所以这样就可以了。
如果您希望所有这些都保留在 class 体内,您可以使用带有 if constexpr
的函数模板,而不是专门的 class 模板。代码也更少:
template <VecIndex I>
constexpr auto vecForIndex()
{
if constexpr (I == VecIndex::first) return &Vecs::vec1;
else if constexpr (I == VecIndex::second) return &Vecs::vec2;
}
然后:
static void workSpecificVec() {
work<vecForIndex<VecIndex::first>()>();
}
这在 -std=c++14
struct Vecs
{
enum class VecIndex { first, second };
std::vector<int> vec1, vec2;
template <VecIndex> struct vecForIndex;
template <> struct vecForIndex<VecIndex::first> {
static constexpr auto vecPtr = &Vecs::vec1;
};
template <> struct vecForIndex<VecIndex::second> {
static constexpr auto vecPtr = &Vecs::vec2;
};
template <std::vector<int> Vecs::*vecPtr>
static void work() {}
static void workSpecificVec() {
work<&Vecs::vec1>();
}
};
但是这个版本的 workSpecificVec 没有:
static void workSpecificVec() {
work<vecForIndex<VecIndex::first>::vecPtr>();
}
后者确实使用 -std=c++17
进行编译。
为什么?
P.S。 gcc 和 msvc 均未使用任何标准编译上述内容。 IIUC 在 class 范围 have been allowed since c++14 中的显式专业化,所以这似乎是编译器或我的理解的问题。
class 范围内的显式特化是 C++17 的一个特性 (CWG 727 was not a DR against C++14, it was new for C++17) that gcc just doesn't implement yet (this is gcc bug 85282)。
这里还有另一个 C++17 特性在发挥作用——扩大了哪些类型的参数可以用作非类型模板参数(N4198 for explanation, N4268 用于措辞)。在 C++14 中,vecForIndex<VecIndex::first>::vecPtr
不是允许的非类型模板参数,因为 只有 成员指针的允许参数(如您的示例)是:
&T::X
形式的精确表达式(就像字面上的语法)- 计算为空指针值的任意常量表达式。
就是这样。 vecForIndex<VecIndex::first>::vecPtr
两者都不是,所以它在 C++14 中的格式不正确。在 C++17 中,对非类型模板参数的限制要宽松得多,所以这样就可以了。
如果您希望所有这些都保留在 class 体内,您可以使用带有 if constexpr
的函数模板,而不是专门的 class 模板。代码也更少:
template <VecIndex I>
constexpr auto vecForIndex()
{
if constexpr (I == VecIndex::first) return &Vecs::vec1;
else if constexpr (I == VecIndex::second) return &Vecs::vec2;
}
然后:
static void workSpecificVec() {
work<vecForIndex<VecIndex::first>()>();
}