模板核心头膨胀和 c++17 中 cpp 文件中 std::hash 的显式特化

问题描述 投票:0回答:2

假设我有一个核心库,我试图在其中为我的核心类添加

std::hash
的显式专业化,包括
Foo
类(以及许多其他我也想专门化
std::hash
的类)。

Foo.h

struct Foo
{
    ...
    size_t _id;
};

KeyHashing.h,与 Foo 位于同一个库中

#include "Foo.h"
#include <unordered_map>

namespace std
{
    template <>
    struct hash<Foo>
    {
        size_t operator()(const Foo& t) const noexcept; // Link error, unresolved external when used in client code
    };
    // extern template struct hash<Foo>; // error C2039 '()': is not a member of 'std::hash<Foo>'
}

担心在我的标头中添加模板定义,这些模板定义将在它们包含的翻译单元中进行编译,并可能增加我的总 exe 大小和编译时间,我希望

operator()
的实现位于 KeyHashing.cpp 中(在核心库中)并且看起来像这样。

KeyHashing.cpp

#include "Foo.h"

// https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x
template <typename T, typename... Rest>
void HashCombine(size_t& seed, const T& v, Rest... rest)
{
    std::hash<T> hasher;
    seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    (HashCombine(seed, rest), ...);
}

namespace std {
size_t hash<Foo>::operator()(const Foo& t) const noexcept // error C2039 when used with extern template struct hash<Foo> in header.
{
    std::size_t ret = 0;
    HashCombine(ret, t._id);
    return ret;
}
}

在我的客户端代码(核心库之外)中,我将

std::unordered_map
与我的内部核心类无缝地用作键。

那么2个问题:

  1. 上面提到的代码膨胀是不是一个错误的假设?
  2. 如果不是,我该如何编译/链接它而不会出现未解决的问题 外部同时保留 .cpp 中的定义?当然,因为在标头中发送定义效果很好。

更新

上面的代码没有链接,有错误

11>MyClass.obj : error LNK2019: unresolved external symbol "public: unsigned __int64 __cdecl std::hash<struct Foo>::operator()(struct Foo const &)const " (??R?$hash@UFoo@@@std@@QEBA_KAEBUFoo@@@Z) referenced in function "public: unsigned __int64 __cdecl std::_Uhash_compare<struct Foo,struct std::hash<struct Foo>,struct std::equal_to<struct Foo> >::operator()<struct Foo>(struct Foo const &)const " (??$?RUFoo@@@?$_Uhash_compare@UFoo@@U?$hash@UFoo@@@std@@U?$equal_to@UFoo@@@3@@std@@QEBA_KAEBUFoo@@@Z)

正如评论中提到的,exe 大小可能会或可能不会受到 ODR 的影响,但编译时间与任何其他指标一样相关,所以我在这里缺少任何语法吗?

c++ stl c++17 template-specialization
2个回答
1
投票

您不能使类模板的实现脱离线,也不能使(完整)专业化的实现脱离线。将专业化委托给一个函数(非模板),然后您可以离线实现该函数。像这样的东西(为了简洁而省略):

KeyHashing.h:

size_t HashFoo(const Foo&) noexcept;

namespace std
{
    template <>
    struct hash<Foo>
    {
        size_t operator()(const Foo& t) const noexcept
        { return HashFoo(t); }
    };
}

KeyHashing.cpp:

size_t HashFoo(const Foo& t) noexcept
{
    // your implementation goes here
}

0
投票

好吧,事实证明我的特殊问题是缺少我在核心库中定义的operator()

__declspec(dllexport)
。因此,虽然 j6t 的答案可以工作,但事实证明我能够有一个脱离线的专业化实现。

© www.soinside.com 2019 - 2024. All rights reserved.