部分特化类的成员定义
我正在尝试定义部分专门化的类模板的成员函数,但是不同的编译器对我可以做什么以及为什么有截然不同的看法。
让我们慢慢来,从适用于所有主要编译器(all = 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();
}
Godbolt 上的示例:gcc、clang、msvc
很酷,但我想将成员函数的声明和定义分开。让我们将定义移到专门的类之一。示例:gcc、clang、msvc。GCC 和 MSVC 都可以正常工作,但 Clang 无法将成员定义正确匹配到正确的声明。
不用担心,反正我从来没有打算使用 Clang。让我们也尝试将其他专门定义与其声明分开。示例:gcc、clang、msvc。GCC 继续交付,Clang 给出了更多相同的错误,MSVC 抱怨我重新定义了一个成员......
谁是对的?我对部分模板专业化有基本的误解吗?我如何让它在 MSVC 上工作?
回答
谁是对的?
GCC 接受所有情况是正确的,而 Clang 和 MSVC 拒绝它们是错误的;部分特化的类外定义应通过等效的模板头:s与其声明匹配,其中包括约束(如果有)。
所述模板头的主模板的Foo是不等同于模板头的它两个约束部分特例的,和主模板的定义应THEREFOR与外的类定义不冲突约束专业化的成员函数其中。
我对部分模板专业化有基本的误解吗?
不,不是从这个问题的外观来看。
我如何让它在 MSVC 上工作?
由于 MSVC 和 Clang 一样,似乎(目前)在区分类外定义的模板头:s方面存在问题(特别是在通过约束部分专门化类模板时显示),您需要在编译时避免使用后者使用 MSVC(目前)。由于可能没有相关的 MSVC 错误报告,您可能需要考虑提交一份。
细节
根据[temp.spec.partial.general]/4部分专业化可能确实受到限制:
部分特化可能会受到限制([temp.constr])。[示例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[...] — 结束示例]
只要主模板的声明在它之前。
我们可能会将您的示例最小化为以下内容:
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: 错误:没有定义的
f类的外S<T>定义void S<T>::f() {} ~~~~~~^
含义 Clang 解释了 out-of-class 定义
template <C T>
void S<T>::f() {}
因为具有与主模板等效的模板头,这是错误的,因为模板头包含模板参数:s,而模板参数:s又包含类型参数:s,而类型参数:s又包含类型约束:s,如果有的话。
template-head :s的等价性由[temp.over.link]/6 控制,其中包括约束 [强调我的]:
如果它们的模板参数列表具有相同的长度,则两个模板头是等效的,相应的模板参数是等效的,并且都使用类型约束声明,如果模板参数声明为类型约束,并且如果要么模板头有一个requires-clause,它们都有requires-clauses并且相应的约束表达式是等价的。
最后,尽管非规范,两个实施例2的[temp.mem]和
[...] 可以在其类定义或类模板定义之内或之外定义成员模板。定义在其类模板定义之外的类模板的成员模板应使用与类模板
等效的模板头指定,后跟与成员模板等效的模板头([temp.over.关联])。[...][ 示例 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>— 结束示例]
和实施例2的[temp.class.general] :
[ 示例 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— 结束示例]
显示受约束的类模板特化的成员函数的类外定义示例。
这尤其是 Clang 错误:
- 错误 48020 - 不符合定义约束成员函数被拒绝
我不知道 MSVC 是否有类似的错误报告。