问题
I'm pretty new in move and lvalue semantics. And I have the impression I'm doing it wrong. Here
the code I want to be able to write once FunctContainer
is implemented:
std::function<double(double)> f = [](double x){return (x * x - 1); };
FunctContainer fc1 = FunctContainer(f);
FunctContainer fc2 = FunctContainer([](double x){return (x * x - 1); });
I want to write FunctContainer
's ctors so that the lifetime of the function stored in fc1
is the one of f
and the lifetime in fc2
of the contained function is the lifetime of fc2
itself.
I have written something (see below) but I'm not really satisfied (I got it wrong).
This is correct c++ but wrong behavior: f_
expires after the call to the constructor when f_
is an rvalue.
class FunctContainerWRONG{
public:
IntegrandNotProjecting(const std::function<double(double)>& function)
: f_(function){}
IntegrandNotProjecting(const std::function<double(double)>&& function)
: f_(std::move(function)){}
const std::function<double(double)>& f_;
private:
};
This looks awful at me and probably is not correct c++ but is intended to explain in pseudocode what the desired behavior looks like. If possible I want to avoid to constuct a brand new object and I just want to let my object "persist":
class FunctContainer{
public:
FunctContainer(const std::function<double(double)>& function)
: f_p(nullptr),
f_(function){}
FunctContainer(const std::function<double(double)>&& function)
: f_p()std::make_shared<std::function<double(double)>>(function)),
f_(*f_p){}
private:
std::shared_ptr<std::function<double(double)>> f_p;
const std::function<double(double)>& f_;
};
回答1:
Forwarding references and reference collapsing rules can help you achieve this easily.
template <typename T>
struct FunctContainer
{
T _f;
template <typename TFwd>
FunctContainer(TFwd&& f) : _f{std::forward<TFwd>(f)} { }
};
template <typename T>
auto makeFunctContainer(T&& f) -> FunctContainer<T>
{
return {std::forward<T>(f)};
}
When you invoke
makeFunctContainer
with an lvalue,T
will be an lvalue reference. This means that you'll store an lvalue reference insideFucntContainer
.When you invoke
makeFunctContainer
with an rvalue,T
will be a value. This means that you'll store a value insideFucntContainer
.
Example usage:
auto fc0 = makeFunctContainer(std::move(f0)); // `fc0` owns `f0`
auto fc1 = makeFunctContainer(f1); // `fc1` stores a reference to `f1`
回答2:
Since C++17 there is class template argument deduction with user-defined deduction guides (Class Template Argument Deduction). With that you can solve your problem as follows:
#include <utility>
#include <iostream>
template<typename FnT>
struct FunctContainer {
constexpr FunctContainer(FnT f) : f_ { std::forward<FnT>(f) } {}
FnT f_;
};
template<typename FnT>
FunctContainer(const FnT& f) -> FunctContainer<const FnT&>;
template<typename FnT>
FunctContainer(FnT&& f) -> FunctContainer<FnT>;
int
main(
) {
auto f { [] { std::cout << "Hello, world!" << std::endl; } };
FunctContainer f1 { f }; // stores reference to f in f1
FunctContainer f2 { [] { std::cout << "Hello, world!" << std::endl; } }; // moves f into f2
f1.f_();
f2.f_();
return 0;
}
来源:https://stackoverflow.com/questions/47591455/take-a-reference-if-lvalue-and-make-a-copy-if-rvalue-i-e-make-rvalue-persistent