Barton-Nackman Trick
Inject non-template friend functions into a namespace via class template instantiation, discoverable exclusively through ADL at the point of use.
Barton-Nackman Tricksince C++98A technique that defines free functions as in-class friend definitions inside a class template, causing the compiler to inject a concrete, non-template function into the enclosing namespace upon instantiation β discoverable only via argument-dependent lookup.
Overview
When you write a friend function definition inside a class template body, the compiler does not produce a function template. At the moment the class template is instantiated with a concrete type, it injects a concrete, non-template free function into the namespace that encloses the class template. That function is invisible to ordinary unqualified lookup β it surfaces only when ADL examines the namespaces associated with one of its argument types.
Two properties set this apart from an equivalent function template written outside the class:
Non-template functions win overload resolution. A concrete function outranks a function template when both are viable candidates. This lets a class template provide type-specific operator implementations that take precedence over any more general template already in scope.
Each instantiation produces an independent function. Widget<int> and Widget<double> each inject their own operator< into the enclosing namespace. They do not share parameterised template machinery; each is a fully resolved, non-template overload.
The idiom is most often combined with the Curiously Recurring Template Pattern (CRTP) to build mixin base classes that donate operator sets to derived types. The derived class simply inherits from the base, and ADL finds the injected operators whenever objects of that type appear as function arguments.
Syntax
template<typename Derived>
class EqualityComparable {
// In-class friend definition: injected as a non-template free
// function when EqualityComparable<X> is instantiated.
// Not findable by ordinary unqualified lookup β only via ADL.
friend bool operator==(const Derived& lhs, const Derived& rhs)
{
return lhs.equals(rhs);
}
friend bool operator!=(const Derived& lhs, const Derived& rhs)
{
return !(lhs == rhs);
}
};The friend keyword does double duty: it grants the function access to the class's private members, and it causes the definition to be placed in the enclosing namespace rather than treated as a member function.
Examples
Comparison mixin via CRTP
A base class donates six comparison operators given only two primitives on the derived type.
template<typename Derived>
class TotallyOrdered {
friend bool operator==(const Derived& a, const Derived& b) { return a.equals(b); }
friend bool operator!=(const Derived& a, const Derived& b) { return !a.equals(b); }
friend bool operator< (const Derived& a, const Derived& b) { return a.less_than(b); }
friend bool operator<=(const Derived& a, const Derived& b) { return !(b < a); }
friend bool operator> (const Derived& a, const Derived& b) { return b < a; }
friend bool operator>=(const Derived& a, const Derived& b) { return !(a < b); }
};
class Temperature : public TotallyOrdered<Temperature> {
public:
explicit Temperature(double kelvin) : kelvin_(kelvin) {}
bool equals(const Temperature& o) const { return kelvin_ == o.kelvin_; }
bool less_than(const Temperature& o) const { return kelvin_ < o.kelvin_; }
private:
double kelvin_;
};
void demo() {
Temperature boil{373.15}, freeze{273.15};
bool b1 = boil > freeze; // true β ADL finds operator> injected for Temperature
bool b2 = freeze <= boil; // true
bool b3 = boil == boil; // true
}Temperature gains six operators without declaring a single one. ADL discovers them because Temperature is an argument and its associated class TotallyOrdered<Temperature> lives in the same namespace as the injected functions.
Strong scalar with per-tag arithmetic
Because each tag produces a distinct instantiation, arithmetic operators for different strong typedefs are completely separate non-template functions β mixing them is a compile error, not a runtime surprise.
template<typename T, typename Tag>
class StrongScalar {
public:
explicit StrongScalar(T v) : value_(v) {}
T get() const { return value_; }
friend StrongScalar operator+(StrongScalar a, StrongScalar b)
{
return StrongScalar{a.value_ + b.value_};
}
friend StrongScalar operator-(StrongScalar a, StrongScalar b)
{
return StrongScalar{a.value_ - b.value_};
}
friend StrongScalar operator*(StrongScalar a, T scalar)
{
return StrongScalar{a.value_ * scalar};
}
friend bool operator==(StrongScalar a, StrongScalar b) { return a.value_ == b.value_; }
private:
T value_;
};
struct MetreTag {};
struct KilogramTag {};
using Metres = StrongScalar<double, MetreTag>;
using Kilograms = StrongScalar<double, KilogramTag>;
void demo() {
Metres a{10.0}, b{5.0};
Metres c = a + b; // OK
Metres d = a * 2.0; // OK
// Metres e = a + Kilograms{1}; // Error: no viable operator+
}swap customisation point
The pattern is the canonical way to provide a type-specific swap that the standard library will find via ADL.
template<typename T>
class Buffer {
public:
explicit Buffer(std::size_t cap)
: data_(new T[cap]), cap_(cap) {}
friend void swap(Buffer& a, Buffer& b) noexcept // C++11 noexcept
{
using std::swap;
swap(a.data_, b.data_);
swap(a.cap_, b.cap_);
}
private:
T* data_;
std::size_t cap_;
};
void example() {
Buffer<int> x{16}, y{32};
swap(x, y); // ADL resolves to the friend β O(1), no copies
std::swap(x, y); // std::swap calls the friend via ADL since C++11
}Best Practices
Define operators as in-class friends, not as out-of-class function templates. The Barton-Nackman form keeps the operator body adjacent to the type it serves, eliminates the need to repeat template parameters at the definition site, and ensures each concrete type gets its own concrete overload rather than sharing a generic template.
Use CRTP + Barton-Nackman for operator mixins pre-C++20. A TotallyOrdered<Derived> base is the idiomatic pattern for donating a full comparison suite. In C++20 and later, auto operator<=>(const T&) const = default; covers most cases and should be preferred when the comparison is member-wise or key-based.
Provide swap customisation through this mechanism. Any type with non-trivial resources should inject a friend void swap(T&, T&) noexcept in-class. Standard algorithms use using std::swap; swap(a, b); which triggers ADL, finding the friend over the generic std::swap.
Keep injected function bodies thin. Each instantiation emits its own copy of the function body (though it is implicitly inline). Expensive logic belongs in a private static helper that all instantiations can share through ordinary function calls.
Common Pitfalls
Ordinary unqualified lookup never finds injected friends. Qualified calls or contexts where ADL is suppressed will silently fail to find the function.
template<typename T>
void correct(T a, T b) {
bool r = a < b; // ADL active β finds injected operator<
}
template<typename T>
void broken(T a, T b) {
bool r = operator<(a, b); // Qualified call suppresses ADL β not found
}Dependent contexts can strip the associated namespaces. When a type is passed as a template argument through another template layer, the associated namespaces for ADL may not include the one where the friend was injected. Test ADL resolution explicitly when building deeply generic code.
One-definition rule still applies. Because the injected function originates from a template, it is implicitly inline. But if you somehow provide diverging bodies in different translation units (e.g., through partial specialisations with conflicting in-class friends for the same type), the resulting ODR violation is undefined behaviour with no guaranteed diagnostic.
C++20 spaceship supersedes simple comparison mixins. For types whose ordering is entirely determined by a single member or a straightforward key, <=> with = default (C++20) replaces the six-operator CRTP mixin cleanly. The Barton-Nackman approach remains the right tool for heterogeneous comparisons, custom ordering semantics, or codebases targeting pre-C++20 standards.
See Also
reference/idioms/adl-customizationβ the argument-dependent lookup mechanism that makes injected friend functions findable; essential background for understanding why this trick worksstd::rel_ops(deprecated in C++20) β the standard library's earlier approach to deriving relational operators from==and<, now superseded byoperator<=>operator<=>(C++20) β three-way comparison that generates the full comparison suite from a single definition, the modern replacement for comparison mixins in the common case