std::make_unique()
(and similar functions) have a little problem:
#include <cstdio>
#include <memory>
using namespace std;
struct S
{
S() { printf("ctor
"); }
~S() { printf("dtor
"); }
S(S const&) { printf("cctor
"); }
S(S&&) { printf("mctor
"); }
};
S foo() { return S(); }
int main()
{
{
printf("--------------- case 1 ---------------
");
unique_ptr<S> s1 = make_unique<S>( foo() );
}
{
printf("--------------- case 2 ---------------
");
unique_ptr<S> s2 { new S( foo() ) };
}
}
Output:
--------------- case 1 ---------------
ctor
mctor
dtor
dtor
--------------- case 2 ---------------
ctor
dtor
As you see we have an extra move that can be avoided. Same problem exists with emplace()
in optional/variant/etc -- if object gets returned by other function, you have to move it.
This can be addressed with a trick:
#include <cstdio>
#include <optional>
using namespace std;
struct S
{
S() { printf("ctor
"); }
~S() { printf("dtor
"); }
S(S const&) { printf("cctor
"); }
S(S&&) { printf("mctor
"); }
template<class F, enable_if_t<is_same_v<invoke_result_t<F>, S>>...>
S(F&& f) : S(forward<F>(f)()) {}
};
S foo() { return S(); }
int main()
{
optional<S> s;
s.emplace( []{ return foo(); } );
}
This avoids unnecessary move (enable_if hides constructor unless f()
returns an instance of S). You effectively end up constructing your values inside of std::variant
/std::optional
/etc via a call to your constructing function.
This fix has a little problem -- adding a constructor breaks aggregate initialization. See example. I.e. if given structure had no constructor and you add one -- you can no longer initialize it like this:
struct D
{
float m;
S s;
// adding new constructor here will break existing bar() functions
};
D bar() { /*...lots of code with multiple return statements...*/ return {2.0, foo()}; }
Question: Is there a way around this problem? Something that doesn't introduce new constructors...
I'd like to be able to efficiently put my structures into optional/variant/shared_ptr-block/etc without breaking (rather non-trivial) code that creates them.
Edit: All MSVC versions can't handle exceptions escaping from Barry's factory
. See details here.
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…