对于使用聚合初始化的结构,避免在make_unique / make_shared / emplace / etc中进行额外移动 [英] Avoiding extra move in make_unique/make_shared/emplace/etc for structures that use aggregate initialization

查看:92
本文介绍了对于使用聚合初始化的结构,避免在make_unique / make_shared / emplace / etc中进行额外移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

std :: make_unique()(以及类似的函数)有些问题

std::make_unique() (and similar functions) have a little problem:

#include <cstdio>
#include <memory>

using namespace std;

struct S
{
    S()         { printf("ctor\n"); }
    ~S()        { printf("dtor\n"); }
    S(S const&) { printf("cctor\n"); }
    S(S&&)      { printf("mctor\n"); }
};

S foo() { return S(); }

int main()
{
    {
        printf("--------------- case 1 ---------------\n");
        unique_ptr<S> s1 = make_unique<S>( foo() );
    }

    {
        printf("--------------- case 2 ---------------\n");
        unique_ptr<S> s2 { new S( foo() ) };
    }
}

输出:

--------------- case 1 ---------------
ctor
mctor
dtor
dtor
--------------- case 2 ---------------
ctor
dtor

如您所见,我们还有一个可以避免的额外举措。 emplace()存在问题c>在可选/变量/等中-如果对象被其他函数返回,则必须移动它。

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.

这可以是以一种技巧解决了

#include <cstdio>
#include <optional>

using namespace std;

struct S
{
    S()         { printf("ctor\n"); }
    ~S()        { printf("dtor\n"); }
    S(S const&) { printf("cctor\n"); }
    S(S&&)      { printf("mctor\n"); }

    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(); } );
}

这避免了不必要的动作(enable_if隐藏了构造函数,除非 f()返回S的实例。您实际上通过调用,在 std :: variant / std :: optional / etc内构造了值您的构造函数。

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...

我希望能够有效地将我的结构放入可选/ variant / shared_ptr-block /等中,而不会破坏(宁可非-

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 :所有MSVC版本都无法处理从Barry的<$ c $转义的异常c>工厂。在此处中查看详情>。

Edit: All MSVC versions can't handle exceptions escaping from Barry's factory. See details here.

推荐答案

不是在您的类型中添加带有工厂函数的构造函数,而是使用将转换运算符转换为您的类型。使用C ++ 17,只需很少的工作:

Instead of adding a constructor to your type that takes a factory function, instead create a new external factory object with a conversion operator to your type. With C++17, that takes minimal work:

template <class F>
struct factory {
    F f;

    operator invoke_result_t<F&>() { return f(); }
};

template <class F>
factory(F ) -> factory<F>;

对于您先前的示例, S 不会不再需要受限的构造函数。您可以改为:

For your earlier example, S doesn't need the constrained constructor anymore. You would instead do:

optional<S> s;
s.emplace( factory{[]{ return foo(); }} ); // or really just factory{foo}

仅打印 ctor dtor 。由于我们不会以任何方式修改 S ,因此我们也可以在汇总中使用它,例如 D

Which prints just ctor and dtor. Since we're not modifying S in any way, we could use this in aggregates as well - like D.

这篇关于对于使用聚合初始化的结构,避免在make_unique / make_shared / emplace / etc中进行额外移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆