部分特化的成员定义 类
Member definition of partially specialized classes
我正在尝试定义部分专用 class 模板的成员函数,但不同的编译器对允许我做什么以及为什么有截然不同的看法。
让我们慢慢来,从适用于所有主要编译器(所有 = gcc、clang 和 msvc)的东西开始:
#include <concepts>
#include <type_traits>
template <class T>
concept Integer
= std::is_same_v<T,int> || std::is_same_v<T,unsigned int>;
template <class T>
concept FloatingPoint
= std::is_same_v<T,float> || std::is_same_v<T,double>;
template <class T>
struct Foo
{
T get() {return 0;}
};
template <Integer T>
struct Foo<T>
{
T get(){ return 0; }
};
template <FloatingPoint T>
struct Foo<T>
{
T get(){ return 0; }
};
int main()
{
Foo<char>().get();
Foo<int>().get();
Foo<float>().get();
}
很好,但我想分开成员函数的声明和定义。让我们移动定义其中一个专门的classes。
示例:gcc, clang, msvc。 GCC 和 MSVC 都可以正常工作,但 Clang 无法将成员定义正确匹配到正确的声明。
不用担心,反正我从来没有打算使用 Clang。让我们也尝试将其他专门定义与其声明分开。示例:gcc, clang, msvc。 GCC 继续提供,Clang 给出了更多相同的错误,MSVC 抱怨我重新定义了一个成员...
谁是对的?我对部分模板专业化有一个根本性的误解吗?我如何让它在 MSVC 上运行?
Who is right?
GCC 接受所有情况是正确的,而 Clang 和 MSVC 拒绝它们是错误的; class 之外的偏特化定义应通过等效的 template-head:s 与其声明相匹配,其中包括约束(如果有的话)。
Foo
主模板的template-head不等同于两个模板的template-head它的约束部分特化,因此主模板的定义不应与它的约束特化的成员函数的 class 外定义冲突。
Do I have a fundamental misconception about partial template specialization?
不,从这个问题的外观来看不是。
How do I get this working on MSVC?
因为 MSVC 和 Clang 一样,似乎(目前)在区分 template-head:s 和 class 之外的定义时存在问题(特别是部分显示时通过约束专门化 class 模板),您需要在使用 MSVC 编译时避免后者(目前)。由于可能没有相关的 MSVC 错误报告,您可能需要考虑提交一份。
详情
根据 [temp.spec.partial.general]/4 部分专业化可能确实受到限制:
A partial specialization may be constrained ([temp.constr]). [Example
2:
template<typename T> concept C = true;
template<typename T> struct X { };
template<typename T> struct X<T*> { }; // #1
template<C T> struct X<T> { }; // #2
[...] — end example]
只要the declaration of the primary template precedes it.
我们可能会将您的示例最小化为以下内容:
template <typename>
concept C = true;
template <typename T>
struct S;
template <C T>
struct S<T> { void f(); };
template <C T>
void S<T>::f() {}
GCC 接受但 Clang 拒绝
prog.cc:12:12: error: out-of-line definition of f
from class S<T>
without definition
void S<T>::f() {}
~~~~~~^
意思是 Clang 解释了超出class定义的
template <C T>
void S<T>::f() {}
有 template-head that is equivalent to that of the primary template, which is wrong, as the template-head includes template-parameter:s which in turn includes type-parameter:s which in turn includes type-constraints:s,如果有的话。
template-head:s 的等价 受 [temp.over.link]/6 约束,其中包括约束 [强调我的]:
Two template-heads are equivalent if their template-parameter-lists have the same length, corresponding template-parameters are equivalent and are both declared with type-constraints that are equivalent if either template-parameter is declared with a type-constraint, and if either template-head has
a requires-clause, they both have requires-clauses and the
corresponding constraint-expressions are equivalent.
最后,尽管是非规范的,Example 2 of [temp.mem] and
[...] A member template can be defined within or outside its class
definition or class template definition. A member template of a class
template that is defined outside of its class template definition
shall be specified with a template-head equivalent to that of the class template followed by a template-head equivalent to that of
the member template ([temp.over.link]). [...]
[ Example 2:
template<typename T> concept C1 = true;
template<typename T> concept C2 = sizeof(T) <= 4;
template<C1 T> struct S {
template<C2 U> void f(U);
template<C2 U> void g(U);
};
template<C1 T> template<C2 U>
void S<T>::f(U) { } // OK
template<C1 T> template<typename U>
void S<T>::g(U) { } // error: no matching function in S<T>
— end example]
和 [temp.class.general] 的示例 2:
[ Example 2:
// ...
template<typename T> concept C = true;
template<typename T> concept D = true;
template<C T> struct S {
void f();
void g();
void h();
template<D U> struct Inner;
};
template<C A> void S<A>::f() { } // OK: template-heads match
template<typename T> void S<T>::g() { } // error: no matching declaration for S<T>
template<typename T> requires C<T> // ill-formed, no diagnostic required: template-heads are
void S<T>::h() { } // functionally equivalent but not equivalent
template<C X> template<D Y>
struct S<X>::Inner { }; // OK
— end example]
显示受限 class 模板特化的 class 成员函数定义之外的示例。
这尤其是 Clang 错误:
我不知道 MSVC 是否有类似的错误报告。
我正在尝试定义部分专用 class 模板的成员函数,但不同的编译器对允许我做什么以及为什么有截然不同的看法。
让我们慢慢来,从适用于所有主要编译器(所有 = gcc、clang 和 msvc)的东西开始:
#include <concepts>
#include <type_traits>
template <class T>
concept Integer
= std::is_same_v<T,int> || std::is_same_v<T,unsigned int>;
template <class T>
concept FloatingPoint
= std::is_same_v<T,float> || std::is_same_v<T,double>;
template <class T>
struct Foo
{
T get() {return 0;}
};
template <Integer T>
struct Foo<T>
{
T get(){ return 0; }
};
template <FloatingPoint T>
struct Foo<T>
{
T get(){ return 0; }
};
int main()
{
Foo<char>().get();
Foo<int>().get();
Foo<float>().get();
}
很好,但我想分开成员函数的声明和定义。让我们移动定义其中一个专门的classes。 示例:gcc, clang, msvc。 GCC 和 MSVC 都可以正常工作,但 Clang 无法将成员定义正确匹配到正确的声明。
不用担心,反正我从来没有打算使用 Clang。让我们也尝试将其他专门定义与其声明分开。示例:gcc, clang, msvc。 GCC 继续提供,Clang 给出了更多相同的错误,MSVC 抱怨我重新定义了一个成员...
谁是对的?我对部分模板专业化有一个根本性的误解吗?我如何让它在 MSVC 上运行?
Who is right?
GCC 接受所有情况是正确的,而 Clang 和 MSVC 拒绝它们是错误的; class 之外的偏特化定义应通过等效的 template-head:s 与其声明相匹配,其中包括约束(如果有的话)。
Foo
主模板的template-head不等同于两个模板的template-head它的约束部分特化,因此主模板的定义不应与它的约束特化的成员函数的 class 外定义冲突。
Do I have a fundamental misconception about partial template specialization?
不,从这个问题的外观来看不是。
How do I get this working on MSVC?
因为 MSVC 和 Clang 一样,似乎(目前)在区分 template-head:s 和 class 之外的定义时存在问题(特别是部分显示时通过约束专门化 class 模板),您需要在使用 MSVC 编译时避免后者(目前)。由于可能没有相关的 MSVC 错误报告,您可能需要考虑提交一份。
详情
根据 [temp.spec.partial.general]/4 部分专业化可能确实受到限制:
A partial specialization may be constrained ([temp.constr]). [Example 2:
template<typename T> concept C = true; template<typename T> struct X { }; template<typename T> struct X<T*> { }; // #1 template<C T> struct X<T> { }; // #2
[...] — end example]
只要the declaration of the primary template precedes it.
我们可能会将您的示例最小化为以下内容:
template <typename>
concept C = true;
template <typename T>
struct S;
template <C T>
struct S<T> { void f(); };
template <C T>
void S<T>::f() {}
GCC 接受但 Clang 拒绝
prog.cc:12:12: error: out-of-line definition of
f
from classS<T>
without definitionvoid S<T>::f() {} ~~~~~~^
意思是 Clang 解释了超出class定义的
template <C T>
void S<T>::f() {}
有 template-head that is equivalent to that of the primary template, which is wrong, as the template-head includes template-parameter:s which in turn includes type-parameter:s which in turn includes type-constraints:s,如果有的话。
template-head:s 的等价 受 [temp.over.link]/6 约束,其中包括约束 [强调我的]:
Two template-heads are equivalent if their template-parameter-lists have the same length, corresponding template-parameters are equivalent and are both declared with type-constraints that are equivalent if either template-parameter is declared with a type-constraint, and if either template-head has a requires-clause, they both have requires-clauses and the corresponding constraint-expressions are equivalent.
最后,尽管是非规范的,Example 2 of [temp.mem] and
[...] A member template can be defined within or outside its class definition or class template definition. A member template of a class template that is defined outside of its class template definition shall be specified with a template-head equivalent to that of the class template followed by a template-head equivalent to that of the member template ([temp.over.link]). [...]
[ Example 2:
template<typename T> concept C1 = true; template<typename T> concept C2 = sizeof(T) <= 4; template<C1 T> struct S { template<C2 U> void f(U); template<C2 U> void g(U); }; template<C1 T> template<C2 U> void S<T>::f(U) { } // OK template<C1 T> template<typename U> void S<T>::g(U) { } // error: no matching function in S<T>
— end example]
和 [temp.class.general] 的示例 2:
[ Example 2:
// ... template<typename T> concept C = true; template<typename T> concept D = true; template<C T> struct S { void f(); void g(); void h(); template<D U> struct Inner; }; template<C A> void S<A>::f() { } // OK: template-heads match template<typename T> void S<T>::g() { } // error: no matching declaration for S<T> template<typename T> requires C<T> // ill-formed, no diagnostic required: template-heads are void S<T>::h() { } // functionally equivalent but not equivalent template<C X> template<D Y> struct S<X>::Inner { }; // OK
— end example]
显示受限 class 模板特化的 class 成员函数定义之外的示例。
这尤其是 Clang 错误:
我不知道 MSVC 是否有类似的错误报告。