Search code examples
c++templateslanguage-lawyerfriendfriend-function

Are non-template friends of template classes instantiated?


Consider the following code :

//Allows to automatically define post in/de-crement operators from their pre- version
template<typename T>
struct Arithmetic
{
    //Not a template?
    friend constexpr auto operator++(T& l, int)
    { auto old = l; ++l; return old; }


    friend constexpr auto operator--(T& l, int)
    { auto old = l; --l; return old; }
};

//Only defines increment
struct Foo : Arithmetic<Foo>
{
    int val;
    Foo& operator++() { ++val; return *this; }
};


int main(int argc, char* argv[])
{
    Foo f;
    f.val = 12;
    ++f;
    f++;

    return 0;
}

If I tried to define the post-decrement operator "manually" (outside of Arithmetic), I would get an error on --l; because the pre-decrement operator is not defined. As non-template friends of template classes are apparently not considered template functions, I would expect the same behaviour.

But as it is, the code compiles just fine for C++17 (at least on msvc and gcc). Why is that so? Is this kind of function a specific case of non-template function that still gets instanciated?

What parts of the standard allow or prevent me from doing such things?


Solution

  • A non-template friend function defined in a template class is a templated entity.

    From the C++ 20 Standard (13.1 Preamble)

    8 A templated entity is

    (8.1) — a template,

    (8.2) — an entity defined (6.2) or created (6.7.7) in a templated entity,

    (8.3) — a member of a templated entity,

    (8.4) — an enumerator for an enumeration that is a templated entity, or

    (8.5) — the closure type of a lambda-expression (7.5.5.1) appearing in the declaration of a templated entity

    [Note: A local class, a local variable, or a friend function defined in a templated entity is a templated entity. —end note]

    It is instantiated when it is required. So in this class definition

    //Only defines increment
    struct Foo : Arithmetic<Foo>
    {
        int val;
        Foo& operator++() { ++val; return *this; }
    };
    

    the templated entity

    friend constexpr auto operator--(T& l, int)
    { auto old = l; --l; return old; }
    

    is not instantiated.

    In the case when the friend function only declared in the template class struct Arithmetic and is defined outside the class for its specialization then the compiler will issue an error because the operator --l is not declared in the class Foo.