cpp:S3470 for overloading std::hash

  • ALM used Azure DevOps
  • CI system used Azure DevOps
  • Scanner command used when applicable (private details masked) 4.1.0, build-wrapper, mixed solution
  • Languages of the repository Visual Studio 2026: C++23, C#
  • Error observed: Specialization of std::hash for user-defined type is reported as an issue.
    From “Why is this an issue”:

A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

  • Steps to reproduce
namespace std
{
    template<>
    struct hash<OperationInfo>
    {
        std::size_t operator()(const OperationInfo& info) const noexcept
        {
            std::size_t h1 = std::hash<std::string>{}(info.Name);
            std::size_t h2 = std::hash<int>{}(std::to_underlying(info.Geometry));
            return h1 ^ (h2 << 1);
        }
    };
}

Hi @milbrandt,

I tried to reproduce your issue, with the following compilable code (extracted from yours):

#include <unordered_map>

class OperationInfo {};
namespace std
{
    template<>
    struct hash<OperationInfo>
    {
        std::size_t operator()(const OperationInfo& ) const noexcept
        {
            return 0;
        }
    };

}

And as expected, the issue is not raised.
Could you please send us a reproducer?

To generate the reproducer file:

  • Search in the analysis log for the full path of the source file for which you want to create a reproducer (here, you want to generate the reproducer of a source file that includes the header where hash is specialized). You will have to use exactly this name (same case, / or \…)
  • Add the reproducer option to the scanner configuration:
    sonar.cfamily.reproducer=“Full path to the .source file”
  • Re-run the scanner to generate a file named sonar-cfamily-reproducer.zip in the project folder.
  • Please share this file. If you think this file contains private information, let us know, and we’ll send you a private message that will allow you to send it privately.

Hi @JolyLoic
I will try to reduce the solution and reproduce it in a smaller context.

As a workaround I removed namespace std and implement it with fully quallified names (avoid ADL). Then no longer S3470 was reported. We use Visual Studio 2026 but still toolset v143.

template<>
struct std::hash<MyBaseNamespace::ComputeEngines::OperationInfo>
{
    std::size_t operator()(const MyBaseNamespace::ComputeEngines::OperationInfo& info) const noexcept
    {
        std::size_t h1 = std::hash<std::string>{}(info.Name);
        std::size_t h2 = std::hash<int>{}(std::to_underlying(info.Geometry));
        return h1 ^ (h2 << 1);
    }
};

Yes, we plan to add an example in the rule description that uses this form, because we think it is more convenient that opening the std namespace.

Hi @JolyLoic,

I now reduced everything to a single file. In Visual Studio, the error list is empty, but in SonarCloud still the S3470 is reported.

Generating reproducer file failed.

##[error]12:54:35.97 ERROR:
The sonar.cfamily.reproducer property was set but no matching file was found. The property was set to:
‘D:_1\7\s\source\WT.NXG.ComputeEngines\AbstractionsDataTypes.ixx’

The file is available - I could open it with this path on the build server and there was also a hit in the cache. The starting of the path I can’t change it is from the agents variable $(Build.SourcesDirectory).

12:59:29.353  INFO: [2/2] Module scan cache hit for: D:/_1/35/s/source/WT.NXG.ComputeEngines/AbstractionsDataTypes.ixx
12:59:29.356  INFO: Module dependencies for std=C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.44.35207\modules\std.ixx: []

I can now rerun with verbose, hopefully this also helps and send you the reduced repo.

You should write the path exactly as it appears in the log, in this case, with forward slashes:

It is expected that this rule does not raise in SonarQube for IDE: It is system level, and means nothing when analyzing files in isolation.

I’ll try it tomorrow - and I have to force for a specific build agent. The number in the path D:_1\7\s depends on the build agent and therefore in the build definition is always used $(Build.SourcesDirectory). But this would be another topic.

Good morning @JolyLoic, finally the reproducer file was created. I renamed the extension as tarx.xz is blocked for uploading.

sonar-cfamily-reproducer.tarxz (907.6 KB)

I will open another thread regarding generating reproducer files.

Hi,

On a side note, you’re the second person this week to struggle with the upload. I’ve (finally!) authorized uploads of .xz.

 
Ann

1 Like

Hi @milbrandt,

Thank you for the reproducer, it allowed me to understand the situation: In your code, the std namespace does not only contain the specialization for std::hash (which is fine), but also a using directive.

Since a using directive is a declaration, it is undefined behavior, which is why the issue is raised.

1 Like

Thanks @JolyLoic for the C++ lesson.

I have to admit, I didn’t took that into account based on the message and description “Why is this an issue?” While reviewing, it was only a shortcut to avoid writing the fully qualified names of the user defined types.

2 Likes

I did not really know it either 5 minutes before I answered you :slight_smile:

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.