Что означает const lambda?

#c #c 11 #lambda #constants

#c #c 11 #лямбда #константы

Вопрос:

 #include <iostream>
int foo(int i)
{
    const auto a = [amp;i](){ i = 7; return i * i; };
    a();
    return i;
}
int main()
{
    std::cout << foo(42) << std::endl;
    return 0;
}
 

Это компилирует( g -std=c 11 -Wall -Wextra -Wpedantic main.cpp ) и возвращает 49 . Что меня удивляет, потому что, объявляя a объект constant , я ожидал i , что на него будут ссылаться как const intamp; . Очевидно, что это не так, почему?

Комментарии:

1. Какая часть вашего кода фиксируется j с помощью const ref ? Я не понимаю проблемы. Вы создали функцию, которая принимает i , игнорирует ее значение, делает его 7, возводит в квадрат, затем возвращает результат (49) … именно это и произошло! Все, что вы делаете, j это кратковременно сохраняете 49 в именованной переменной, прежде чем возвращать ее. Не могли бы вы уточнить свое намерение, пожалуйста.

2. Теперь у вас вообще нет имени переменной j . Все, что вы делаете с этим измененным кодом, это устанавливаете i значение 7, возвращаете 49, а затем отбрасываете это значение 49.

3. Потому что вы захвачены неконстантной ссылкой. Это все равно, что спрашивать, почему on может изменять вещи через a int *const .

4. Я не понимаю ваш вопрос, как указано в @LightnessRacesinOrbit.

5. Интересно, каков эффект const спецификатора перед лямбдой.

Ответ №1:

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

 #include <iostream>
int foo(int i)
{
    struct F {
      int amp;i;
      int operator()() const { i = 7; return i * i; }
    };
    const F a {i};
    a();
    return i;
}
int main()
{
    std::cout << foo(42) << std::endl;
    return 0;
}
 

F имеет int amp; ссылочный элемент i . const F невозможно изменить данные его экземпляра, но модификация i не является модификацией данных его экземпляра. Изменение данных его экземпляра приведет к повторной привязке i к другому объекту (что в любом случае запрещено).

Ответ №2:

 [amp;i](){ i = 7; return i * i; }
 

в основном эквивалентно

 class Lambda
{
public:
    Lambda(intamp; arg_i) : i(arg_i) {}

    auto operator() () const { i = 7; return i * i;}
private:
    intamp; i;
};
 

И тогда у вас есть:

 const Lambda a(i);
a();
 

И const Lambda не будет продвигать своего члена на const intamp; i; , но intamp; const i; что эквивалентно intamp; i; .

Ответ №3:

Когда вы вводите i его, он записывается как тип, которым он является.

Итак, внутренне он имеет intamp; . Значение const перед объявлением переменной замыкания ничего не меняет для лямбда-выражения.

У вас есть 2 варианта решения этой проблемы:

 const int i = 5;
auto b = [amp;i]() { i  ; }; //error on i  
 

Таким образом, a const intamp; будет захвачен.

Если вы не можете изменить i по каким-либо причинам, вы можете сделать это в c 14

 int i = 5;
auto b = [i = static_cast<const intamp;>(i)]() { i  ; }; //error on i  
 

Это приводит intamp; к преобразованию в a const intamp; и будет сохранено как таковое в лямбде. Хотя, как вы можете видеть, это намного более подробно.

Комментарии:

1. Если он не хочет менять i , возможно, лучше сделать это копией.

2. @krzaq да, но это было бы полезно в тех случаях, когда у вас есть большие объекты, которые вы не хотите копировать. В случае int , если это не требуется.

Ответ №4:

В коде, который вы дали:

 int foo(int i)
{
    const auto a = [amp;i](){ i = 7; return i * i; };
    a();
    return i;
}
 

Вы не назначаете после инициализации своей постоянной лямбда-функции. Следовательно, const в данном контексте это мало что значит.

Комментарии:

1. Я этого не делал, но ваш ответ, похоже, не имеет смысла для меня.

2. @krzaq в моем ответе указано: сначала OP инициализировал постоянную лямбду. И тогда он не присвоил этой лямбде другое значение, const модификатор здесь не играет большой роли.. Что вы в этом не понимаете?

3. Я тоже. @Vorac: Nothing... it means you wouldn't be able to alter a, or its members, but you're not trying to do that here anyway. Were you expecting it to magically change the internal intamp; to a const intamp;? – Lightness Races in Orbit 9 mins ago Ваш ответ касается первого пункта, в то время как моя борьба связана со вторым. Я отредактировал вопрос, пытаясь прояснить это.

4. @Vorac чего ты не понимаешь.. пожалуйста, укажите.

5. @krzaq в заголовке буквально задается вопрос «Что означает const lambda?» . В этом контексте .. это ничего не значит, как я уже говорил!

Ответ №5:

То, что вы объявили, поскольку const это не контекст вашей анонимной функции или лямбда-выражения и его параметров, а только ссылка на это лямбда-выражение : const auto a .

Следовательно, вы не можете изменить значение вашей ссылки на лямбда-выражение a , потому что оно равно const , но его параметр, передаваемый по ссылке, amp;i , может быть изменен в контексте лямбда-выражения.

Комментарии:

1. a не является ссылкой.

2. Я не углублялся в детали языка.. Я подумал, что этого достаточно, чтобы дать абстрактное указание на роль переменной, присвоенной адресу лямбда-выражения, а именно: «ссылка» на лямбда-выражение или анонимную функцию…

3. Это не ссылка.

Ответ №6:

Если я правильно понимаю, вопрос в том, почему вам разрешено мутировать i , даже если a is const и предположительно содержит ссылку на i as a member .

Ответ заключается в том, что по той же причине вам разрешено делать это с любым объектом — присвоение i не изменяет экземпляр lambda, оно изменяет объект, на который он ссылается.

Пример:

 class A
{
public:
    A(intamp; y) : x(y) {} 
    void foo(int a) const { x = a; } // But it's const?!
private:
    intamp; x;
};

int main()
{
    int e = 0;
    const A a(e);
    a.foo(99);
    std::cout << e << std::endl;
}
 

Это компилируется и выводит «99», потому foo что это не изменение члена a , это изменение e .
(Это немного сбивает с толку, но помогает понять, какие объекты изменяются, и не обращать внимания на то, как они называются.)

Эта «постоянная, но не совсем» природа const является очень распространенным источником путаницы и раздражения.

Именно так ведут себя указатели, где это, очевидно, не так:

 class A
{
public:
    A(int* y) : x(y) {} 
    void foo(int a) const { *x = a; } // Doesn't modify x, only *x (which isn't const).
private:
    int* x;
};