我可以专门化前向声明的模板吗?

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 的措辞。所以这是未定义的行为。