热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

完美的转发和构造函数

如何解决《完美的转发和构造函数》经验,为你挑选了1个好方法。

我试图理解完美转发和构造函数的相互作用.我的例子如下:

#include 
#include 


template
using disable_if_same_or_derived =
  std::enable_if_t<
    !std::is_base_of<
      A,
      std::remove_reference_t
    >::value
  >;


template
class wrapper {
  public:
    // perfect forwarding ctor in order not to copy or move if unnecessary
    template<
      class T0,
      class = disable_if_same_or_derived // do not use this instead of the copy ctor
    > explicit
    wrapper(T0&& x)
      : x(std::forward(x))
    {}

  private:
    T x;
};


class trace {
  public:
    trace() {}
    trace(const trace&) { std::cout <<"copy ctor\n"; }
    trace& operator=(const trace&) { std::cout <<"copy assign\n"; return *this; }
    trace(trace&&) { std::cout <<"move ctor\n"; }
    trace& operator=(trace&&) { std::cout <<"move assign\n"; return *this; }
};


int main() {
  trace t1;
  wrapper w_1 {t1}; // prints "copy ctor": OK

  trace t2;
  wrapper w_2 {std::move(t2)}; // prints "move ctor": OK

  wrapper w_3 {trace()}; // prints "move ctor": why?
}

我希望我wrapper没有任何开销.特别是,当将一个临时编组到包装器中时w_3,我希望该trace对象可以直接在适当的位置创建,而不必调用移动ctor.但是,有一个移动ctor调用,这使我认为临时创建然后移动.移动ctor为什么叫?怎么不打电话呢?



1> bolov..:

我希望跟踪对象可以直接在适当的位置创建,而无需调用移动ctor.

我不知道为什么你会这么想.转发就是这样做的:移动或复制1).在您的示例中,您创建一个临时的trace(),然后转发将其移入x

如果要构建T对象,则需要将参数传递给构造T,而不是T要移动或复制的对象.

创建一个就地构造函数:

template 
wrapper(std::in_place_t, Args&&... args)
    :x{std::forward(args)...}
{}

然后像这样调用它:

wrapper w_3 {std::in_place};
// or if you need to construct an `trace` object with arguments;
wrapper w_3 {std::in_place, a1, a2, a3};

在另一个答案中处理OP的评论:

@bolov让我们忘记完美转发一分钟.我认为问题在于我希望在最终目的地构建一个对象.现在,如果它不在构造函数中,现在可以保证可以使用有保证的复制/移动省略(此处移动和复制几乎相同).我不明白为什么在构造函数中这是不可能的.我的测试用例证明它不会按照现行标准发生,但我认为这不应该由标准指定并由编译器实现.我错过了什么关于ctor的特别之处?

在这方面,关于ctor绝对没有什么特别之处.您可以使用简单的自由函数查看完全相同的行为:

template 
auto simple_function(T&& a)
{
    X x = std::forward(a);
    //  ^ guaranteed copy or move (depending on what kind of argument is provided
}

auto test()
{
    simple_function(X{});
}

以上示例与您的OP类似.你可以看到simple_function你的包装器构造函数和我的局部x变量类似于你的数据成员的模拟wrapper.在这方面机制是相同的.

为了理解为什么你不能直接在本地范围内构造对象simple_function(或者作为你的包装器对象中的数据成员),你需要理解保证copy elision在C++中是如何工作的17我推荐这个极好的答案.

总结一下这个答案:基本上prvalue表达式不会实现一个对象,而是可以初始化一个对象.在使用表达式初始化对象之前,尽可能长时间地保留表达式(从而避免一些复制/移动).请参阅链接的答案以获得更深入而友好的解释.

当表达式用于初始化参数simple_foo(或构造函数的参数)时,您将被迫实现对象并丢失表达式.从现在开始,您不再拥有原始的prvalue表达式,您有一个已创建的物化对象.现在需要将此对象移动到您的最终目标 - 我的本地x(或您的数据成员x).

如果我们稍微修改我的例子,我们可以看到有保证的复制省略:

auto simple_function(X a)
{
    X x = a;
    X x2 = std::move(a);
}


auto test()
{
    simple_function(X{});
}

如果没有省略,事情会是这样的:

X{}创建一个临时对象作为参数simple_function.让我们来称呼它Temp1

Temp1现在被移动(因为它是一个prvalue)到参数asimple_function

a被复制(因为a是左值)x

a被移动(因为std::move强制转换a为xvalue)x2

现在用C++ 17保证复制省略

X{}不再在现场实现物体.取而代之的是表达式.

参数asimple_function现在可以通过从初始化X{}表达.不涉及也不需要复制或移动.

其余的现在是一样的:

a 被复制到 x1

a 被搬进去了 x2

您需要了解的内容:一旦您命名了某些内容,就必须存在某些内容.这个令人惊讶的简单原因是,一旦你有一个名字的东西,你可以多次引用它.在另一个问题上看到我的答案.您已将参数命名为wrapper::wrapper.我已经命名了参数simple_function.这是您丢失prvalue表达式以初始化该命名对象的那一刻.


如果你想使用C++ 17保证的副本省略,你不喜欢就地方法,你需要避免命名的东西:)你可以用lambda做到这一点.我经常看到的成语,包括在标准中,是就地方式.由于我没有看到野外的lambda方式,我不知道我是否会推荐它.无论如何它在这里:

template class wrapper {
public:

    template 
    wrapper(F initializer)
        : x{initializer()}
    {}

private:
    T x;
};

auto test()
{
    wrapper w = [] { return X{};};
}

在C++ 17中,即使X已删除了复制构造函数和移动构造函数,也不会复制和/或移动它.对象将在它的最终目的地构建,就像你想要的那样.


1)如果使用得当,我正在谈论转发习语.std::forward只是一个演员.


推荐阅读
  • Igotthiscode(IknowitsinSpanishIcantranslateifneeded)wheretheygivemethefunctionS ... [详细]
  • AtonepointIhadlookedatimplementingaclasstemplateinC++thatwouldsupportanEnumthatwo ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 字符串的题目用库函数往往能大大简化代码量介绍几个常用的C的字符串处理库函数strtok()原型char*strtok(chars[],constchar*delim); ... [详细]
  • C++语言学习(六)——二阶构造模式
    C++语言学习(六)——二阶构造模式一、构造函数的问题构造函数存在的问题:A、构造函数只提供自动初始化成员变量的机会B、不能保证初始化逻辑 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • [置顶]        C++类的构造函数与析构函数的调用顺序
    1构造函数的调用顺序[1]构造函数按此顺序执行工作:按声明顺序调用基类和成员构造函数。如果类派生自虚拟基类,则会将对象的虚拟基指针初始化。如果类具有或继承了虚函数,则会将对象的虚函数指针初始化。 ... [详细]
author-avatar
手机用户2502884005
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有