c++language-lawyerc++20

Is it mandatory to infer the type of the default template argument on each instantiation?


Having this code, GCC and MSVC are both happy with it, when clang complains. Any two lambdas must have different types, which makes me think that clang "caches" the type of the default template argument (but only if the class itself is a template).

UPDATE: What I mean here is I expect that every time I instantiate the function template it should infer the type of the default argument. But what truly happens (in the third test) is clang instantiate the function template only once (on the first use). This is is inconsistent not olny with both other major compilers but even with clang itself. As you can see such a behavior only applies if the function template defined/declared in a class template (doesn't matter if class/function template arguments are dependent or not). In other two tests (free function and method in non-class-template) the type is inferencing on each instantiation.

Is there any formal rule for that? Or it's undefined/unspecified/implementation specific behavior?

#include <type_traits>

template <typename T = decltype([](){})>
T f();

struct Test1 {
    template <typename T = decltype([](){})>
    T f();
};

template <typename = void>
struct Test2 {
    template <typename T = decltype([](){})>
    T f();
};

int main()
{
    static_assert(!std::is_same_v<decltype(f()), decltype(f())>);

    Test1 test1;
    static_assert(!std::is_same_v<decltype(test1.f()), decltype(test1.f())>);

    Test2 test2;
    static_assert(!std::is_same_v<decltype(test2.f()), decltype(test2.f())>); // GCC and MSVC ok with this line, clang complains
}

live example


Solution

  • This is unfortunately just a gap in the specification. The standard doesn't explain how any of these 3 examples should behave. [expr.prim.lambda.closure]/1 says:

    The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.

    The meaning of "unique" has never been elaborated. Nor does the specification of default template arguments explain anything useful here.

    Because a default template argument can reference earlier template arguments in the same template parameter list, it would appear that a default template argument may have to be instantiated multiple times for the same template (at least, when different values are supplied to the preceding template arguments). From there it's not too much of a leap to conclude that perhaps a default template argument is re-instantiated every time it's used, and if it contains a lambda then you get a different lambda every time (and if it doesn't contain a lambda then you just get what you get). But again, the standard doesn't explicitly spell this out.