#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;
};