Функторы захвата и пересылки лямбд - PullRequest
1 голос
/ 06 марта 2019

Я пытаюсь лучше понять, как лямбды захватывают функторы и как они связаны с идеальной пересылкой.

Код, который я включил, является минимальным примером. В моем реальном коде используются шаблоны с переменным числом аргументов, но при этом фиксируется большинство ошибок / недоразумений, которые у меня были. Я получаю разные результаты на разных компиляторах (Visual Studio 2017 vs gcc 4.9.3 и 7.2.0).

Похоже, что "проблемная" строка является экземпляром has_functor с указателем на функцию, т. Е. Делает y в main.

Я пробовал 3 разные версии кода, изменив аргумент lambda, который инициализирует toil_and_trouble.

  • версия 1: захват по ссылке и вперед ...

    [&](double d)->double{
      return calls(std::forward<F>(f),d);  
    }
    

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

  • версия 2: это выглядит как компромисс в том, что мы принимаем по значению и затем продвигаемся соответствующим образом:

    [=](double d) mutable ->double{
      return calls(std::forward<F>(f),d);  
    }
    

    Это отлично работает на VS, но не на gcc. Я также думаю, что при пересылке f, потенциально в качестве значения x, лямбда может потерять захваченный функтор, то есть его значение может измениться при пересылке и использовании для создания других функторов.

    Но ошибка компилятора, которую я вижу, на самом деле возникает только тогда, когда F выводится как double (&) (double, double), но не в двух других случаях.

  • версия 3: Я подумал, что может быть проблема с пересылкой, поэтому я просто не переадресовал:

    [=](double d) mutable ->double{
      return calls(f,d);  
    }
    

    Это работает на VS, но не на gcc, опять же.

Может ли кто-нибудь прокомментировать эти 3 разные версии с точки зрения того, где может возникнуть UB, и как правильно управлять созданием экземпляров с помощью ссылок на функции и значений?

Цитаты из стандарта C ++ также могут быть полезны.

код ниже:

#include<utility>
#include<functional>  
#include<iostream>

// functors

double foo(double, double) { return 0.; }

struct G{
  double operator()(double,double) const{ return 0.; }
};

// helper

template<typename Fun>
double calls(Fun&& f, double d){
  return f(0,d);   
}

class has_functor{
private:
  std::function<double(double)> toil_and_trouble;  
public:
  template<typename F>
  has_functor(F&& f) : /* have tried multiple versions of this */
    toil_and_trouble( [&](double d)->double{
      return calls(std::forward<F>(f),d);  
  } ) {}
};


int main(){
  auto x = has_functor(G{});    
  auto y = has_functor(foo);
  auto z = has_functor([](double,double)->double{return 0.;});    
}
...