模板 类 的非模板朋友是否被实例化?

Are non-template friends of template classes instantiated?

考虑以下代码:

//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;
}

如果我尝试定义 post-递减运算符 "manually"(在 Arithmetic 之外),我会在 --l; 上出现错误,因为预递减运算符未定义。由于模板 类 的非模板朋友显然不被视为模板函数,我希望有相同的行为。

但实际上,代码编译适用于 C++17(至少在 msvc 和 gcc 上)。为什么呢?这种函数是非模板函数的一个特例,它仍然被实例化吗?

标准的哪些部分允许或阻止我做这样的事情?

If I tried to define the post-decrement operator "manually" (outside of Arithmetic), I would get an error on --l;

这些确实不是模板函数,因此您必须手动为每个 T 提供函数,这里是 Foo 的版本:

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

在模板中定义的非模板友元函数 class 是模板化实体。

来自 C++ 20 标准(13.1 序言)

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]

需要的时候实例化。所以在这个 class 定义中

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

模板化实体

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

未实例化。

如果 friend 函数仅在模板 class struct Arithmetic 中声明并且在 class 之外定义以用于其特化,则编译器将发出错误,因为运算符--l 未在 class Foo 中声明。