2

I implemented std::hash<std::string> in the usual way, by specializing the std::hash template. But then I realized that these should already be provided by the <string> header (https://www.cppreference.com/w/cpp/string/basic_string/hash.html). Unexpectedly, the program compiled (using both the latest MSVC and GCC-15.1) and it worked using my implementation.

Was I supposed to get a 'template already specialized' error?

namespace std
{
    template <>
    struct hash<std::string>
    {
        static auto operator()(const std::string&) noexcept -> std::size_t
        {
          return 0uz;
        }
    };
}  // namespace std
6
  • Adding to namespace std (except for specific cases like specialization for your custom types) invokes UB. Commented Nov 19 at 11:20
  • Ok, thanks, so the problem is that it's not a custom type. Commented Nov 19 at 11:21
  • Yes, see my complete answer below. Commented Nov 19 at 11:30
  • 2
    Sadly for this kind of UB "no diagnostic is required", since compiler quite often is unable to detect this and linker has logic to resolve such possible conflicts. Commented Nov 19 at 11:31
  • The term for what you are doing also answers your question - this code is Ill Formed, No Diagnostic Required. Commented Nov 19 at 16:08

3 Answers 3

8

Why is a custom specialization of std::hash<std::string> allowed?

TL;DR:
It is not. Your code invokes undefined-behavior.

More info:

The general rule for namespace std:

It is undefined behavior to add declarations or definitions to namespace std or to any namespace nested within std, with a few exceptions noted below.

I.e. it is generally invalid to add to namespace std.

One of the exceptions involves template specialization, but mentions that:

It is allowed to add template specializations for any standard library class template to the namespace std only if the declaration depends on at least one program-defined type and the specialization satisfies all requirements for the original template, except where such specializations are prohibited.

(emphasis is mine)

In your case the specialization is for a standard library type (std::string) and not a program-defined type and therefore the exception does not apply.

The bottom line is that the code invokes UB (undefined-behavior).


Regarding getting an error from the compiler:

As @DrewDormann commented, this is a case where no error etc. is required from the compiler, which is known as IFNDR - Ill Formed, No Diagnostic Required.

Sign up to request clarification or add additional context in comments.

Comments

5

On top of what wohlstad explains in their answer (you are not permitted to specialize std::hash for std::string), this wouldn't be a compiler error.

Was I supposed to get a 'template already specialized' error?

No. The specializations you link to aren't for std::string. They are partial specializations for std::basic_string<char, std::char_traits<char>, A> for allocators of type A (and there are different partial specializations for different character types). The allocator is not yet fixed for those partial specializations. Hence, a std::hash<std::string> would be more specialized (if it was allowed).

The situation is similar to

template <typename T> struct foo {};
template <typename T> struct bar;
template <typename T> struct bar<foo<T>>{};    // library partial specialization

template <> struct bar<foo<int>>{};            // your full specialization

int main()
{
    bar<foo<int>> b;                           // ok. Your full specialization
}

9 Comments

How the specialization are provided sounds like something specific to the implementation, right?
what do you mean? The implementation must provide the partial specializations. I think an implmentation could, in principle, also provide the full specialization (which is btw one reason you are not permitted do provide it yourself)
my point is just that if we put aside the fact that you may not add to namespace std, the presence of the partial specializations, the one you link to, does not prevent you from providing a full specialization. In other words, if you were allowed to add to namespace std and to specialize library templates, no compiler error is to be expected for a full specialization even when there are other partial specializations. You say you expect such error, hence I clarified that it is not an error
What I mean, is that you gave an explanation of why I didn't get a compiler error in the case of a specific implementation, which only partially specializes things. If there was an implementation providing a full specialization I would get the compiler error instead (ignoring the fact it's UB).
No I am not talking about a specific implementation. I am refering to what the standard mandates for any implmentation to provide. The partial specializations documented in the link you added in your quesiton, must be present.
and even then. Your question, at least thats how I read it, is "There are these partial specializations, I implement a full specialization. Why is there no error?" And I still think, that irrespective of any other reasons to not get an error, its worthwhile to explain that the reason you expected is false.
"But then I realized that (cppreference.com/w/cpp/string/basic_string/hash.html) these should be already be provided by the string header." Thats wrong. I dont want to pick on it too much, but perhaps there is a misunderstanding. The site you link to does not mention any std::hash<std::string> specialization.
Ok got it, the standard mandates partial specialization not full specializations, right? I thought your explanation referred to a specific implementation (not that it matters as it is UB so anything can happen). My question was more like: "the standard says it specializes (I wrongly thought fully) hashing of strings, why there is no error?"
glad that we understand now :). I admit, I don't know if an implementation is even allowed to fully specialize std::hash for std::string. However, as it has to partially specialize I would be rather surprised if it also had a std::hash<std::string>
3

[namespace.std] #2:

Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace std provided that

- the added declaration depends on at least one program-defined type, and

- the specialization meets the standard library requirements for the original template.

Your specialization doesn't have any program-defined type, so this is undefined behavior.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.