Категория значений переменной const int, захваченная лямбда-выражением

Я пытался понять, когда и когда лямбда с odr по умолчанию для захвата использует переменную с автоматическим сроком хранения, определенным в его окружении (вызвано этим ответом). Исследуя это, я натолкнулся на небольшое любопытство. GCC и Clang, по-видимому, не согласны с категорией значения id-выражения n в следующем коде:

template <typename T> void assert_is_lvalue(const T&) {}
template <typename T> void assert_is_lvalue(const T&&) = delete;

int main() {
    const int n = 0;
    [=] { assert_is_lvalue(n); };
}

Clang успешно компилирует код, а GCC - нет (error: use of deleted function). Который правильный? Или это что-то, что не определено или определяется реализацией?

Предполагается, что привязка ссылки к объекту использует его odr, и это подтверждается удалением лямбда-захвата по умолчанию и наблюдением, что оба компилятора потом жалуются, что n не может быть захвачен безоговорочно без захвата по умолчанию.

Маркировка лямбды как mutable не вносит заметной разницы в вывод компиляторов.

0 ответов

Оказывается, поведение gcc изменилось с gcc-7.5 и выше! Я использовал следующий код, чтобы увидеть, как n фиксируется в лямбда-выражении и какой шаблон соответствует.

#include <iostream>
#include <string>
#include <typeinfo>
#include <type_traits>
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
constexpr std::string_view type_name()
{
    using namespace std;

    #ifdef __clang__
        string_view p = __PRETTY_FUNCTION__;
        return string_view(p.data() + 34, p.size() - 34 - 1);
    #elif defined(__GNUC__)
        string_view p = __PRETTY_FUNCTION__;

        #if __cplusplus < 201402
            return string_view(p.data() + 36, p.size() - 36 - 1);
        #else
            return string_view(p.data() + 49, p.find(';', 49) - 49);
        #endif
    #elif defined(_MSC_VER)
        string_view p = __FUNCSIG__;
        return string_view(p.data() + 84, p.size() - 84 - 7);
    #endif
}

template <typename T> 
void assert_is_lvalue(const T& param) 
{
    std::cout << "  T is " << type_name<T>() 
              << "  param is " << type_name<decltype(param)>() << '\n';
}

//template <typename T> void assert_is_lvalue(const T&&) = delete;
template <typename T> 
void assert_is_lvalue(const T&& param)
{
    std::cout << "  T is " << type_name<T>() 
              << "  param is " << type_name<decltype(param)>() << '\n';
}

int main() 
{
    const int n = 0;
    [=] { 
        std::cout << "  n is " << type_name<decltype(n)>() << '\n';
        assert_is_lvalue(n); 
    }();

    return 0;
}

и вот результаты:

gcc-7.5

n is const int
T is int  param is const int&&

gcc-8.1

n is const int
T is int  param is const int&

вы можете поиграть с кодом здесь.

Другие вопросы по тегам