Я пытаюсь лучше понять, как лямбды захватывают функторы и как они связаны с идеальной пересылкой.
Код, который я включил, является минимальным примером. В моем реальном коде используются шаблоны с переменным числом аргументов, но при этом фиксируется большинство ошибок / недоразумений, которые у меня были. Я получаю разные результаты на разных компиляторах (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.;});
}