【问题标题】:Can I specialize forward declared template?我可以专门化前向声明的模板吗?
【发布时间】:2023-04-08 02:55:02
【问题描述】:

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

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 特化需要在头文件中包含&lt;functional&gt;,然后对其进行特化。这个头文件我很多地方都用到了,&lt;functional&gt;的编译时间还是蛮大的。所以我想将我的专业转移到源 (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 是内联命名空间。这部分回答了这个问题。

【问题讨论】:

    标签: c++ templates template-meta-programming


    【解决方案1】:

    关于A 的一般问题是,是的,它是允许的。显式特化与主模板不相交。它可以被定义为完整的或不完整的,而不管主要是如何定义的。

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

    [namespace.std]

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

    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;
      
      }
      

      另见:Forward Declaration of variables/classes in std namespace

      但一般来说,允许专门化前向声明的模板,但不适用于 std:: 命名空间中的模板:

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

      很遗憾,没有很多诸如&lt;iosfwd&gt; 这样的前向标头,否则这将不是问题。

      【讨论】:

      • 我不确定额外的模板参数是否不违反文档,但将模板放在内联命名空间中不会。 libc++ 做到了。
      • @jaskmar - 我记得我曾经不得不解决的一个编译器错误,它涉及带有附加参数的这种情况。但是在符合标准的编译器中,附加模板参数很好,只要它们有默认参数。声明特化时,任何省略的参数都将被视为在主节点上指定的默认值。
      猜你喜欢
      • 2015-05-26
      • 1970-01-01
      • 2013-01-08
      • 1970-01-01
      • 1970-01-01
      • 2016-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多