Can I specialize forward declared template?
Asked Answered
A

2

6

Can I specialize forward declared template? For example:

template <typename T> class A;

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

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

What do I want to achieve?

As we know, we have to specialize std::hash to use it with some hash-table-based types. Standard std::hash specialization requires to include <functional> in the header file and then specialize it. I use this header file in many places, and the compilation time of <functional> is pretty big. So I want to move my specialization to source (cpp) file.

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

This solution works, but is it legal in terms of ISO standard?

EDIT/NOTE: It doesn't work with libc++ (clang std implementation), because it defines std::hash as std::__1::hash, where __1 is inline namespace. This partially answers the question.

Actuality answered 1/9, 2020 at 8:49 Comment(0)
I
6

The general question about A is that yes, it's allowed. An explicit specialization is disjoint from the primary template. It can be defined as complete or incomplete, regardless of how the primary is defined.

As for your more specific question about std::hash, not it's not alright. You violate

[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.

Forward declaring std::hash is not a declaration of a specialization that depends on a user defined type. It's a plain declaration that falls under the specification of paragraph 1. There is no wording that allows to forward declare std::hash anywhere else in the standard. So this is undefined behavior.

Ingeminate answered 1/9, 2020 at 9:7 Comment(0)
E
2

It is not legal because forward-declaring any templates in the std:: namespace is not allowed. For your particular standard library implementation it might work, but std::hash could have other template parameters beyond just the type of the hash, as long as those have default values. For example:

namespace std {

// this is how hash might be declared in the standard library
template <typename T, bool B = some_trait_v<T>>
struct hash;

}

Also see: Forward Declaration of variables/classes in std namespace

However generally, it is permitted to specialize forward-declared templates, just not for templates in the std:: namespace:

template <typename T>
void f();

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

It is unfortunate there there aren't many forward-headers such as <iosfwd>, otherwise this would be a non-issue.

Energize answered 1/9, 2020 at 9:3 Comment(3)
I am not sure if additional template parameters doesn't violate the documentation, but placing the template in inline namespace doesn't. The libc++ does it.Indefectible
@jaskmar - I recall a compiler bug I once had to work around that involved such a situation with additional parameters. But in a standard conforming compiler, additional template parameters are fine, so long as they have a default argument. When declaring the specialization, any omitted argument is taken to be the default as specified on the primary.Ingeminate
only if not specified otherwise, and standard allows to provide std:hash template specialization for user defined types.Tandem

© 2022 - 2024 — McMap. All rights reserved.