Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it unspecified to instantiate template/lambda in unevaluated context?

I try following code to check if template are instantiated in unevaluated context:

#include "foo.h"

template <typename T = int>
constexpr auto f(int)
// from declaration, foo(std::declval<T>()) is allowed.
// Even if definition would produce errors if instantiated
-> decltype(foo(std::declval<T>()), void(), 42)
{
    return 42;
}
static_assert(f(0) == 42);

with foo as template function: (No errors)

template <typename ...Ts>
void foo(Ts... args)
{
    static_assert(sizeof...(Ts) == 42, "!");
    ((args += ""), ...);
}

Demo

with foo as regular functor: (No errors)

struct Foo
{
    template <typename ...Ts>
    void operator ()(Ts... args) const
    {
        static_assert(sizeof...(args) == 42, "!");
        ((args += ""), ...);
    }
} foo;

Demo

But foo as lambda: (Error)

auto foo = [](auto... args)
{
    static_assert(sizeof...(args) == 42, "!"); // Triggers
    ((args += ""), ...);                       // spotted as invalid: int += const char*
};

Demo

Is it normal that operator() of lamdba is instantiated?

gcc/clang have same behavior.

like image 292
Jarod42 Avatar asked Jul 05 '19 10:07

Jarod42


1 Answers

The lambda case is actually different than the others! You don't specify a return type for the lambda, so it is deduced. For the deduction to happen the lambda has to be instantiated.

This is not the case for the function object, since there you specified the return type to be void. Changing the lambda to return void to avoid the deduction makes gcc/clang happy. :)

auto foo = [](auto... args) -> void // <---
{
    static_assert(sizeof...(args) == 42, "!");
    ((args += ""), ...);
};

And if you change the function object as follows:

struct Foo
{
    template <typename ...Ts>
    auto operator ()(Ts... args) const // <---- placeholder as return type
    {
        static_assert(sizeof...(args) == 42, "!");
        ((args += ""), ...);
    }
} foo;

it does also instantiate Foo::operator() to be able to deduce the return type.

like image 113
Rakete1111 Avatar answered Nov 10 '22 20:11

Rakete1111