部分特化的成员定义 类

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();
}

神栓示例:gcc, clang, msvc

很好,但我想分开成员函数的声明和定义。让我们移动定义其中一个专门的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 是否有类似的错误报告。