More C++ Idioms/Barton-Nackman trick

From Wikibooks, open books for an open world
Jump to: navigation, search

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. 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]

Related Idioms[edit]

References[edit]