2016-03-31 2 views
7

Я попытался увеличиваем локальную переменную из лямбда-выражения:Как увеличить переменную от тела лямбда-функтора?

#include <iostream> 

template<typename T> 
T foo(T t){ 
    T temp{}; 
    [temp]() -> void { 
     temp++; 
    }(); 
    return temp; 
} 

int main() 
{ 
    std::cout<< foo(10) << std::endl; 
} 

DEMO

Но получил следующее сообщение об ошибке:

main.cpp: In instantiation of 'foo(T)::<lambda()> [with T = int]': 

main.cpp:6:6: required from 'struct foo(T) [with T = int]::<lambda()>' 

main.cpp:8:6: required from 'T foo(T) [with T = int]' 

main.cpp:14:23: required from here 

main.cpp:7:13: error: increment of read-only variable 'temp' 

     temp++; 

      ^

Есть ли обходной путь о том, что в C++ 11/14?

ответ

7

temp не может быть изменен, если он был захвачен копией в не изменяемой лямбда.

Вы могли бы захватить temp по ссылке:

template<typename T> 
T foo(T t){ 
    T temp{}; 
    [&temp]() -> void { 
     temp++; 
    }(); 
    return temp; 
} 
4

Если вы хотите, чтобы захватить переменную по значению и изменить его, необходимо отметить лямбда mutable:

[temp]() mutable -> void { 
//  ^^^^^^^ 
    temp++; 
}(); 

Это позволяет организму для изменения параметров, отснятых по значению, и для вызова их функций, не являющихся константами.

+0

Будет ли это просто изменить скопированный один, и не имеет ничего делать с внешней «temp»? http://ideone.com/pCwE3N – songyuanyao

+1

@songyuanyao: да. :-) – Nawaz

+1

@songyuanyao Да, это по-прежнему захватывающая ценность, независимо от изменчивости. – TartanLlama

2

Что вы подразумеваете под «локальной переменной», это немного неоднозначный.

  1. Для увеличения foo «s temp вам нужно capture by reference, так что ваша лямбда будет выглядеть следующим образом: [&temp] { temp++; }
  2. Для увеличения лямбды-х temp вам нужно use the mutable keyword, так что ваша лямбда будет выглядеть следующим образом: [temp]() mutable { temp++; }

это кажется маловероятным, что вы пытаетесь сделать , потому что любые изменения, которые вы делаете, будут потеряны по возвращению лямбды, так как он ничего не возвращает. И в примере, который вы указали, вы больше не используете temp в лямбда.

Так предполагая, что вы пытаетесь сделать , вам необходимо убедиться, что T не ссылка, а не const. В любом из этих примеров будет вам неприятности:

const auto t = 1; 
foo(t); 

или

auto& t = bar; 
foo(t); 

Чтобы избежать обоих из них вы должны определить foo «s temp с decay_t:

decay_t<T> temp{}; 
3

Это C + +14, поэтому вы можете упростить свою лямбду.
Тем не менее, вот некоторые действительные решения (я предполагаю, что вы хотите изменить значение исходной переменной, а не его копии):

  • захвата по ссылке:

    [&temp]() { temp++; }();

(вы должны гарантировать, что время жизни temp результатов один из лямбды)

  • копию назад и вперед:

    temp = [temp = temp]() mutable { return ++temp; }();

(вы не просили, чтобы гарантировать, что время жизни temp исходов один из лямбды)

  • шага назад и :

    temp = [temp{std::move(temp)}]() mutable { temp++; return std::move(temp); }();

(вы не просили, чтобы гарантировать, что время жизни temp результатов один из лямбда)

И так далее ...

+0

Мне очень приятно видеть, что кто-то использует инициализаторы захвата C++ 14, поэтому у вас есть +1. Но из-за того, как используется лямбда, я не думаю, что здесь требуется синтаксис C++ 11. –

+0

@JonathanMee Я знаю, вы правы, но это был хороший шанс их использовать !! :-) – skypjack

+0

честно то, что мне хотелось, чтобы C++ 14 дал нам возможность определять временные ряды в списке захвата, например '[int i {}] {return ++ i; } 'и хотя это обсуждалось, оно все еще недоступно в C++ 17 :( –

Смежные вопросы