Какая из имплементаций права? У всех трёх разное мнение касательно корректности этого кода. 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()
вообще
Быть может, у кого-то есть мысли на этот счёт?