部分特化类的成员定义

我正在尝试定义部分专门化的类模板的成员函数,但是不同的编译器对我可以做什么以及为什么有截然不同的看法。

让我们慢慢来,从适用于所有主要编译器(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 是否有类似的错误报告。


以上是部分特化类的成员定义的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>