Rule A14-7-2 (required, implementation, automated)

Template specialization shall be declared in the same file (1) as the primary template (2) as a user-defined type, for which the specialization is declared.

Rationale

It is undefined behavior, when a compiler sees the (partial or explicit) template specialization after it already has instantiated the primary or less specialized template. Moreover, the case (2) allows compile-time interfaces to be extensible, as developers can safely provide custom specializations e.g. for traits classes or std::hash.

Example

// $Id: A14-7-2.cpp 312645 2018-03-21 11:44:35Z michal.szczepankiewicz $
#include <cstdint>

//in A.hpp

#include <functional>

struct A
{
std::uint8_t x;
};

namespace std {

//compliant, case (2)
//template specialization for the user-defined type
//in the same file as the type declaration
template <>
struct hash<A>
{
size_t operator()(const A& a) const noexcept
{
return std::hash<decltype(a.x)>()(a.x);
}
};

}

//traits.hpp

#include <type_traits>
#include <cstdint>

template <typename T>
struct is_serializable : std::false_type {};

//compliant, case (1)
template <>
struct is_serializable<std::uint8_t> : std::true_type {};

//func.cpp

#include <vector>

//non-compliant, not declared
//in the same file as
//is_serializable class
template <>
struct is_serializable<std::uint16_t> : std::true_type {};

template <typename T, typename = std::enable_if<is_serializable<T>::value>>
std::vector<std::uint8_t> serialize(const T& t)
{
//only a basic stub

return std::vector<std::uint8_t>{t};
}

#include <string>
int main()
{
serialize(std::uint8_t{3});
}

See also

MISRA C++ 2008 [7]: Rule 14-7-2 (Required) For any given template specialization, an explicit instantiation of the template with the template arguments used in the specialization shall not render the program ill-formed.