如何在 C++ 中的 class 主体之外定义专门的 class 方法?
How to define a specialized class method outside of class body in C++?
我有一个模板 class A<T>
及其对整数参数的专门化。 class 及其特化都声明了方法 foo()
,我想在 class 主体之外定义它:
#include <concepts>
template<class T>
struct A { static void foo(); };
template<std::integral T>
struct A<T> { static void foo(); };
template<class T>
void A<T>::foo() {}
template<std::integral T>
void A<T>::foo() {}
int main() { A<int>::foo(); }
GCC 接受此代码。
Clang 打印错误 https://gcc.godbolt.org/z/hYfYGPfMh :
error: type constraint differs in template redeclaration
template<std::integral T>
并且 MSVC 在两个方法定义上打印错误:
error C3855: 'A<T>': template parameter 'T' is incompatible with the declaration
error C2447: '{': missing function header (old-style formal list?)
error C2065: 'foo': undeclared identifier
请建议如何在 class 主体之外定义方法并让所有编译器满意?
我不是 C++ 模板专家,我试过类似下面的方法
template<class T, bool = std::is_integral_v<T>>
struct A
{};
template<class T>
struct A<T, false>
{
static void foo();
};
template<class T>
struct A<T, true>
{
static void foo();
};
template<class T>
void A<T,false>::foo()
{
std::cout << "I am working on a non-integral type" << std::endl;
}
template<class T>
void A<T, true>::foo()
{
std::cout << "I am working on an integral type" << std::endl;
}
int main()
{
A<int>::foo();
A<float> ::foo();
return 0;
}
代码给了我在 MS C++ 编译器上的结果
I am working on an integral type
I am working on a non-integral type
我很确定 MS 和 Clang 编译器在这里都出错了,而 GCC 正在正确编译您的代码。在这些错误在其他编译器中得到修复之前,我建议继续使用概念模式,而不是回到过时的方法。只需使用额外的 class:
解决该错误
#include <concepts>
#include <iostream>
// This is a work-around for using concept specialization of
// classes in conjunction with out-of-body definition of members.
// Only helpful for MSVC and Clang. GCC is properly compiling
// out-of-body concept specializations.
template <typename T>
class A
{
// For MSVC ONLY: the default template seems require being empty
// for this to work, but do fiddle around with it.
// (Also works with MSVC:)
A() = delete;
A(const A&) = delete;
A(A&&) noexcept = delete;
A& operator =(const A&) = delete;
A& operator =(A&&) noexcept = delete;
~A() = delete;
// Clang and GCC can have members just fine:
// static const char* foo();
};
// We use distinct base classes to define our concept specializations of A.
template <std::signed_integral T>
class A_Signed_Integral
{
public:
static const char* foo();
};
template <std::unsigned_integral T>
class A_Unsigned_Integral
{
public:
static const char* foo();
};
// And then we wrap them using the actual concept specializations of A,
// making the specializations effectivel the same class as the base class.
template <std::signed_integral T>
class A<T> :
public A_Signed_Integral<T>
{
public:
using A_Signed_Integral<T>::A_Signed_Integral; // grab all ctors
using A_Signed_Integral<T>::operator =; // an exceptional case
};
template <std::unsigned_integral T>
class A<T> :
public A_Unsigned_Integral<T>
{
public:
using A_Unsigned_Integral<T>::A_Unsigned_Integral;
using A_Unsigned_Integral<T>::operator =;
};
// Out-of-body definitions can be located to another file
template <std::signed_integral T>
inline const char* A_Signed_Integral<T>::foo()
{
return "using A<std::signed_integral T> foo";
}
template <std::unsigned_integral T>
inline const char* A_Unsigned_Integral<T>::foo()
{
return "using A<std::unsigned_integral T> foo";
}
int main()
{
std::cout << A<signed long>::foo() << std::endl;
std::cout << A<unsigned long>::foo() << std::endl;
return 0;
}
(用所有三个编译器测试并且工作正常:see gcc.godbolt.org)
将来,一旦错误被修复,搜索和替换应该相对容易地删除基础 classes,而只使用 A 的概念特化。
编辑:
更新示例以适用于 MSVC,它似乎还不能使用默认模板。
我有一个模板 class A<T>
及其对整数参数的专门化。 class 及其特化都声明了方法 foo()
,我想在 class 主体之外定义它:
#include <concepts>
template<class T>
struct A { static void foo(); };
template<std::integral T>
struct A<T> { static void foo(); };
template<class T>
void A<T>::foo() {}
template<std::integral T>
void A<T>::foo() {}
int main() { A<int>::foo(); }
GCC 接受此代码。
Clang 打印错误 https://gcc.godbolt.org/z/hYfYGPfMh :
error: type constraint differs in template redeclaration
template<std::integral T>
并且 MSVC 在两个方法定义上打印错误:
error C3855: 'A<T>': template parameter 'T' is incompatible with the declaration
error C2447: '{': missing function header (old-style formal list?)
error C2065: 'foo': undeclared identifier
请建议如何在 class 主体之外定义方法并让所有编译器满意?
我不是 C++ 模板专家,我试过类似下面的方法
template<class T, bool = std::is_integral_v<T>>
struct A
{};
template<class T>
struct A<T, false>
{
static void foo();
};
template<class T>
struct A<T, true>
{
static void foo();
};
template<class T>
void A<T,false>::foo()
{
std::cout << "I am working on a non-integral type" << std::endl;
}
template<class T>
void A<T, true>::foo()
{
std::cout << "I am working on an integral type" << std::endl;
}
int main()
{
A<int>::foo();
A<float> ::foo();
return 0;
}
代码给了我在 MS C++ 编译器上的结果
I am working on an integral type
I am working on a non-integral type
我很确定 MS 和 Clang 编译器在这里都出错了,而 GCC 正在正确编译您的代码。在这些错误在其他编译器中得到修复之前,我建议继续使用概念模式,而不是回到过时的方法。只需使用额外的 class:
解决该错误#include <concepts>
#include <iostream>
// This is a work-around for using concept specialization of
// classes in conjunction with out-of-body definition of members.
// Only helpful for MSVC and Clang. GCC is properly compiling
// out-of-body concept specializations.
template <typename T>
class A
{
// For MSVC ONLY: the default template seems require being empty
// for this to work, but do fiddle around with it.
// (Also works with MSVC:)
A() = delete;
A(const A&) = delete;
A(A&&) noexcept = delete;
A& operator =(const A&) = delete;
A& operator =(A&&) noexcept = delete;
~A() = delete;
// Clang and GCC can have members just fine:
// static const char* foo();
};
// We use distinct base classes to define our concept specializations of A.
template <std::signed_integral T>
class A_Signed_Integral
{
public:
static const char* foo();
};
template <std::unsigned_integral T>
class A_Unsigned_Integral
{
public:
static const char* foo();
};
// And then we wrap them using the actual concept specializations of A,
// making the specializations effectivel the same class as the base class.
template <std::signed_integral T>
class A<T> :
public A_Signed_Integral<T>
{
public:
using A_Signed_Integral<T>::A_Signed_Integral; // grab all ctors
using A_Signed_Integral<T>::operator =; // an exceptional case
};
template <std::unsigned_integral T>
class A<T> :
public A_Unsigned_Integral<T>
{
public:
using A_Unsigned_Integral<T>::A_Unsigned_Integral;
using A_Unsigned_Integral<T>::operator =;
};
// Out-of-body definitions can be located to another file
template <std::signed_integral T>
inline const char* A_Signed_Integral<T>::foo()
{
return "using A<std::signed_integral T> foo";
}
template <std::unsigned_integral T>
inline const char* A_Unsigned_Integral<T>::foo()
{
return "using A<std::unsigned_integral T> foo";
}
int main()
{
std::cout << A<signed long>::foo() << std::endl;
std::cout << A<unsigned long>::foo() << std::endl;
return 0;
}
(用所有三个编译器测试并且工作正常:see gcc.godbolt.org)
将来,一旦错误被修复,搜索和替换应该相对容易地删除基础 classes,而只使用 A 的概念特化。
编辑: 更新示例以适用于 MSVC,它似乎还不能使用默认模板。