我能专门化前置声明的模板吗?

6

我能专门化前置声明的模板吗?例如:

template <typename T> class A;

template <>
class A<char> {
    char a[1000];
};

int main()
{
    [[maybe_unused]] A<char> a;
    return 0;
}

我想要达到什么目的?

我们知道,为了在一些基于哈希表的类型中使用它,必须对 std::hash 进行专门化。标准的 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标准的角度来看是否合法?
编辑/注释:它不适用于libc ++(clang std实现),因为它将std :: hash定义为std :: __ 1 :: hash,其中__1是内联命名空间。这部分回答了问题。
2个回答

6

关于A的常见问题是它是被允许的。显式特化与主模板不相交。它可以定义为完整或不完整,无论主模板如何定义。

至于你更具体的关于std::hash的问题,不行。您违反了:

[namespace.std]

1 除非另有说明,否则如果将声明或定义添加到命名空间std或添加到命名空间std中的命名空间,则C++程序的行为未定义。

2 除非明确禁止,否则程序可以向命名空间std添加任何标准库类模板的模板特化,前提是(a)添加的声明依赖于至少一个程序定义的类型,并且(b)特化符合原始模板的标准库要求。

前向声明std::hash不是一个声明,它依赖于用户定义的类型的特化。这是一个落入段落1规范下的普通声明。没有措辞允许在标准的其他地方前向声明std::hash。因此,这是未定义的行为。


2

这是不合法的,因为在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;

}

此外还可以参考:std命名空间中变量/类的前置声明 然而,通常情况下,允许对前置声明的模板进行特化,但不能对std::命名空间中的模板进行特化:
template <typename T>
void f();

// this is perfectly normal and allowed
template <>
void f<int>() {
    // do something ...
}

很不幸,像<iosfwd>这样的前向头文件并不多见,否则这将不是一个问题。


我不确定是否添加额外的模板参数会违反文档规定,但将模板放置在内联命名空间中则不会。libc++ 就是这样做的。 - Mariusz Jaskółka
@jaskmar - 我记得曾经遇到过一个编译器错误,需要解决这种涉及额外参数的情况。但在符合标准的编译器中,只要它们有默认参数,额外的模板参数就没问题。在声明特化时,任何省略的参数都被视为在主模板上指定的默认值。 - StoryTeller - Unslander Monica
只有在标准允许为用户自定义类型提供std:hash模板特化时,才能提供。 - Marek R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接