Порядок оценки аргументов функции и порядок оценки захвата лямбды - PullRequest
0 голосов
/ 26 мая 2018

Похоже, что и порядок оценки аргументов функции , и порядок инициализаторов лямбда-захвата , не указан по стандарту C ++.

(см. http://en.cppreference.com/w/cpp/language/lambda, а также Порядок вычисления в параметрах функции C ++ )

Это вызывает у меня некоторую обеспокоенность из-за того, как оно может взаимодействовать с семантикой перемещения.

Предположим, у меня есть объект типа T, который может иметь конструктор копирования или перемещения, который выдает.Тогда предположим, что у меня есть объект только для перемещения, такой как std::promise.Рассмотрим следующую ситуацию:

T value; // some type that potentially throws when moved or copied
promise<U> pr; // a promise whose result is some type U
future<U> fut = pr.get_future(); 

std::thread(
  [v = std::move(value), pr = std::move(pr)]() {
    try {
      // do some stuff

      pr.set_value(/* whatever */);
    }
    catch (...) { pr.set_exception(std::current_exception()); }
  }
).detach();

// return the future

Теперь у нас есть блок try / catch, который выполняется внутри std::thread, но у нас нет обработки исключений для всего, что могло бы пойти не так при инициализациинить.В частности, что мы можем сделать, если выражение v = std::move(value) в списке лямбда-захвата в итоге выдает исключение?В идеале, мы бы хотели обработать его с помощью блока try-catch, а затем просто позвонить pr.set_exception(...), например:

 try {
    std::thread(
      [v = std::move(value), pr = std::move(pr)]() {
        try {
          // do some stuff

          pr.set_value(/* whatever */);
        }
        catch (...) { pr.set_exception(std::current_exception()); }
      }
    ).detach();
  }
  catch (...) {
    pr.set_exception(std::current_exception()); 
  }

Есть только одна основная проблема: когда мы получимчто касается нашего внешнего блока catch, мы не знаем, было ли уже вызвано выражение pr = std::move(pr), потому что у нас нет никакой гарантии относительно порядка для списка инициализаторов лямбда-захвата.Поэтому, когда мы говорим pr.set_exception(...), мы больше не знаем, является ли наше обещание даже действительным , потому что мы не знаем, было ли это обещание построено на ходу до выражения v = std::move(value) был оценен.

Так, как мы можем обработать случай, когда конструктор перемещения или копирования для T может выдать?

Единственное решение, которое я могу придумать - может быть - это обернутьлямбда в вызове std::bind, например:

std::thread(
  std::bind(
    [v = std::move(value)](promise<U>& pr) {
      // ...
    },
    std::move(pr)
  )
).detach();


Здесь, даже если у нас нет никаких гарантий относительно порядка вычисления аргументов функции,Насколько я понимаю, мы по-прежнему гарантируем, что выражение v = std::move(value) необходимо будет оценить до того, как обещание будет действительно построено, поскольку выражение std::move(pr) фактически не перемещается, создает обещание - оно просто приводит его к R-значение.Обещание будет сконструировано только позже, внутри вызова std::bind, но , а не как результат одного из оцениваемых аргументов функции.

Однако я не совсем уверен в этом решении.Я не уверен, может ли стандарт каким-либо образом разрешить компилятору перемещать-создавать обещание до того, как T будет создано с помощью перемещения / копирования.

Итак, мое решение, использующее std::bind, решает эту проблему?Если нет, как можно решить эту проблему?

1 Ответ

0 голосов
/ 26 мая 2018

Ваш std::bind работает (bind принимает аргументы по ссылке, инициализация объекта замыкания секвенируется до выполнения тела bind, и перемещение из promise обязательно происходит внутри bind).

Это, однако, довольно бессмысленно, поскольку конструктор std::thread уже может передавать произвольные аргументы.

std::thread(
    [v = std::move(value)](promise<U> pr) {
      // ...
    },
    std::move(pr)
).detach();

Обратите внимание, что std::thread передает аргументы как значения r, в отличие от bind.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...