C++ Code Hygiene

Nicolae Vartolomei · 2025/02

Your Rights End Where Mine Begin

Template specializations must reside in the same header file that defines the template or at least one type for which template is specialized.

Therefore, as a library author, you should only specialize templates defined within the same library, or specialize third-party templates for types you have defined. Avoid specializing third-party templates for types defined in other libraries.

Violating this rule is an easy path to ODR violations.

Example violation

// lib.hpp
#pragma once
struct A {};
// lib_ext.hpp
#pragma once

#include "lib.hpp"

#include <functional>

template <> struct std::hash<A> {
  std::size_t operator()(const A &a) const { return 0; }
};
// lib.cpp
#include "lib.h"
#include "lib_ext.h"

#include <iostream>

void f() {
  std::cout << "hashes to " << std::hash<A>{}(A{}) << "\n";
}
// main.cpp
#include "lib.hpp"

#include <iostream>

template<>
struct std::hash<A> {
  std::size_t operator()(const A &a) const { return 42; }
};

int main() {
  std::cout << "hashes to " << std::hash<A>{}(A{}) << "\n";

  return 0;
}

Compile the source files:

$ echo "lib main" | xargs -n1 -I{} clang++ --std=c++23 -c {}.cpp -o {}.o

Link the object files and execute:

$ clang++ main.o lib.o -o main && ./main
hashes to 42

Changing the linking order may produce different results ☢️:

$ clang++ lib.o main.o -o main && ./main
hashes to 0

Adhering to the rule would have produced a more desirable outcome, namely a compilation error.

// lib.hpp
#pragma once

#include <functional>

struct A {};

template <> struct std::hash<A> {
  std::size_t operator()(const A &a) const { return 0; }
};
$ echo "lib main" | xargs -n1 -I{} clang++ --std=c++23 -c {}.cpp -o {}.o
main.cpp:6:13: error: redefinition of 'hash<A>'
    6 | struct std::hash<A> {
      |             ^~~~~~~
./lib.h:7:25: note: previous definition is here
    7 | template <> struct std::hash<A> {
      |                         ^
1 error generated.

Related reading: https://gudok.xyz/inline/