More C++ Idioms/Barton-Nackman trick

Barton-Nackman trick

edit

Intent

edit

Support overloaded operators without relying on namespaces or function template overload resolution.

Also Known As

edit

The inventors originally referred to it as Restricted Template Expansion, though this term has never been widely used.

Motivation

edit

John Barton and Lee Nackman first published this idiom in 1994 in order to work around limitations of the C++ implementations available at the time.[1] Though it is no longer necessary for its original purpose, the current standard retains support for it.

At the time Barton and Nackman originally developed the idiom, C++ did not support overloading of function templates and many implementations still didn't support namespaces. This caused problems when defining operator overloads for class templates. Consider the following class:

template<typename T>
class List {
   // ...
};

The most natural way to define the equality operator is as a non-member function at namespace scope (and since compilers at the time didn't support namespaces, therefore at global scope). Defining operator== as a non-member function means that the two arguments are treated symmetrically, which doesn't happen if one argument is a this pointer to the object. Such an equality operator might look like this:

template<typename T>
bool operator==(List<T> const & lft, List<T> const & rgt) {
   //...
}

However, since function templates couldn't be overloaded at the time, and since putting the function in its own namespace wouldn't work on all platforms, this would mean that only one class could have such an equality operator. Doing the same thing for a second type would cause an ambiguity.

Solution and Sample Code

edit

The solution works by defining an operator in the class as a friend function:

template<typename T>
class List {
 public:
    friend bool operator==(const List<T> & lft,
                           const List<T> & rgt) {
        // ...
    }
};

Instantiating the template now causes a non-template function to be injected into global scope with the argument types being concrete, fixed types. This non-template function can be selected through function overload resolution the same way as any other non-template function.

The implementation can be generalised by providing the friend functions as part of a base class that is inherited from via the Curiously Recurring Template Pattern:

template<typename T>
class EqualityComparable {
public:
    friend bool operator==(const T & lft, const T & rgt) { return lft.equalTo(rgt); }
    friend bool operator!=(const T & lft, const T & rgt) { return !lft.equalTo(rgt); }
};

class ValueType :
    private EqualityComparable<ValueType> {
 public:
    bool equalTo(const ValueType & other) const;
};

Known Uses

edit
edit

See also

edit

References

edit
  1. Barton, John J.; Nackman, Lee R. (1994). Scientific and Engineering C++: An Introduction with Advanced Techniques and Examples. Addison-Wesley. ISBN 0-201-53393-6.