我可以专门化前向声明的模板吗?
Can I specialize forward declared template?
我可以特化前向声明的模板吗?例如:
template <typename T> class A;
template <>
class A<char> {
char a[1000];
};
int main()
{
[[maybe_unused]] A<char> a;
return 0;
}
我想达到什么目的?
正如我们所知,我们必须专门化 std::hash
才能将其用于某些基于散列 table 的类型。标准 std::hash
特化需要在头文件中包含 <functional>
然后对其进行特化。我在很多地方都用到了这个头文件,<functional>
的编译时间还是蛮大的。所以我想将我的专业转移到源 (cpp) 文件。
my_type.hpp:
class my_type {/*...*/};
namespace std {
template <typename T>
struct hash;
template <>
struct hash<my_type>
{
size_t operator()(my_type m) const;
};
} // namespace std
my_type.cpp:
#include "my_type.hpp"
#include <functional>
namespace std {
size_t std::hash<my_type>::operator()(my_type v) const
{
return std::hash<decltype(v.value())>{}(v.value());
}
} // namespace std
此解决方案有效,但它是否符合 ISO 标准?
EDIT/NOTE: 它不适用于 libc++(clang std 实现),因为它将 std::hash
定义为 std::__1::hash
,其中__1
是内联命名空间。这部分回答了问题。
这是不合法的,因为 forward-declaring std::
命名空间中的任何模板都是不允许的。对于您的特定标准库实现,它可能有效,但 std::hash
可以有其他模板参数,而不仅仅是散列类型,只要它们具有默认值即可。例如:
namespace std {
// this is how hash might be declared in the standard library
template <typename T, bool B = some_trait_v<T>>
struct hash;
}
另见:Forward Declaration of variables/classes in std namespace
但是一般来说,允许专门化 forward-declared 模板,只是不允许 std::
命名空间中的模板:
template <typename T>
void f();
// this is perfectly normal and allowed
template <>
void f<int>() {
// do something ...
}
不幸的是 forward-headers 并不多,例如 <iosfwd>
,否则这将是一个 non-issue。
关于 A
的一般问题是,是的,这是允许的。显式特化与主模板不相交。它可以定义为完整的或不完整的,而不管主要是如何定义的。
至于你关于 std::hash
的更具体的问题,不是不行。你违反了
[namespace.std]
1 Unless otherwise specified, the behavior of a C++ program is
undefined if it adds declarations or definitions to namespace std
or
to a namespace within namespace std
.
2 Unless explicitly prohibited, a program may add a template
specialization for any standard library class template to namespace
std
provided that (a) the added declaration depends on at least one
program-defined type and (b) the specialization meets the standard
library requirements for the original template.
前向声明 std::hash
不是依赖于用户定义类型的特化声明。这是一个符合第 1 段规范的简单声明。没有允许在标准中的其他任何地方转发声明 std::hash
的措辞。所以这是未定义的行为。
我可以特化前向声明的模板吗?例如:
template <typename T> class A;
template <>
class A<char> {
char a[1000];
};
int main()
{
[[maybe_unused]] A<char> a;
return 0;
}
我想达到什么目的?
正如我们所知,我们必须专门化 std::hash
才能将其用于某些基于散列 table 的类型。标准 std::hash
特化需要在头文件中包含 <functional>
然后对其进行特化。我在很多地方都用到了这个头文件,<functional>
的编译时间还是蛮大的。所以我想将我的专业转移到源 (cpp) 文件。
my_type.hpp:
class my_type {/*...*/};
namespace std {
template <typename T>
struct hash;
template <>
struct hash<my_type>
{
size_t operator()(my_type m) const;
};
} // namespace std
my_type.cpp:
#include "my_type.hpp"
#include <functional>
namespace std {
size_t std::hash<my_type>::operator()(my_type v) const
{
return std::hash<decltype(v.value())>{}(v.value());
}
} // namespace std
此解决方案有效,但它是否符合 ISO 标准?
EDIT/NOTE: 它不适用于 libc++(clang std 实现),因为它将 std::hash
定义为 std::__1::hash
,其中__1
是内联命名空间。这部分回答了问题。
这是不合法的,因为 forward-declaring std::
命名空间中的任何模板都是不允许的。对于您的特定标准库实现,它可能有效,但 std::hash
可以有其他模板参数,而不仅仅是散列类型,只要它们具有默认值即可。例如:
namespace std {
// this is how hash might be declared in the standard library
template <typename T, bool B = some_trait_v<T>>
struct hash;
}
另见:Forward Declaration of variables/classes in std namespace
但是一般来说,允许专门化 forward-declared 模板,只是不允许 std::
命名空间中的模板:
template <typename T>
void f();
// this is perfectly normal and allowed
template <>
void f<int>() {
// do something ...
}
不幸的是 forward-headers 并不多,例如 <iosfwd>
,否则这将是一个 non-issue。
关于 A
的一般问题是,是的,这是允许的。显式特化与主模板不相交。它可以定义为完整的或不完整的,而不管主要是如何定义的。
至于你关于 std::hash
的更具体的问题,不是不行。你违反了
[namespace.std]
1 Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace
std
or to a namespace within namespacestd
.2 Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace
std
provided that (a) the added declaration depends on at least one program-defined type and (b) the specialization meets the standard library requirements for the original template.
前向声明 std::hash
不是依赖于用户定义类型的特化声明。这是一个符合第 1 段规范的简单声明。没有允许在标准中的其他任何地方转发声明 std::hash
的措辞。所以这是未定义的行为。