Какая из имплементаций права? У всех трёх разное мнение касательно корректности этого кода. MSVC считает, что всё хорошо. GCC не позволяет из тела лямбды вызвать нешаблонный
operator(), но
позволяет вызвать шаблонный (очевидно, это следствие того, как реализован оператор каста к указателю на функцию). Clang же не позволяет вызвать ни тот, ни другой
>> 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
>> The closure type for a lambda-expression has a public inline function call operator (for a non-generic lambda) or function call operator template (for a generic lambda)
>> An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing
— ...
( http://eel.is/c++draft/expr.prim.lambda#closure-1 )
Ни в последнем процитированном пункте, ни во всём разделе, я не нашёл никаких исключений, касающихся
name lookup для лямбд без захвата, т.е. поиск имён в лямбде должен соответствовать поиску имён внутри
unnamed non-union class type, а значит, можно предположить, что в следующем куске кода поведение при вызове
f(3) и
l(3) должно быть эквивалентно:
struct /* unnamed */ {
template<typename Auto>
constexpr int operator()(Auto n) const {
if (n <= 1) return 1;
else return n * operator()(n-1);
}
} f;
void foo() {
constexpr auto l = [](auto n) -> int {
if (n <= 1) return 1;
else return n * operator()(n-1);
};
static_assert(l(3) == f(3));
};
При этом, кажется, при нешаблонном
operator() и GCC, и MSVC оба могут быть правы, допускаю даже, что могут быть правы одновременно. Но я не вижу ни одного аргумента в пользу Clang, который не позволяет вызвать шаблонный
operator() вообще
Быть может, у кого-то есть мысли на этот счёт?