【问题标题】:Perfect forwarding array of objects完美的转发对象数组
【发布时间】:2018-12-27 09:22:53
【问题描述】:

我有一个带有非默认构造函数的类Object,以及一个包含Objects 数组的类ManyObjects。该数组应由用户使用构造函数的参数进行初始化。

然后有一个类ManyObjectsWrapper,它继承自ManyObjects,并且还期望一个数组来初始化ManyObjects

这是我目前正在使用的代码。

代码

#include <iostream>

#define PRINTFN() std::cout << __PRETTY_FUNCTION__ << std::endl

class Object {
  public:
    Object(int number) : number(number) { PRINTFN(); }
    Object(Object &&o) : number(std::move(o.number)) { PRINTFN(); }
    // Object(Object&&) = default;
    void print() { std::cout << "The number is " << number << std::endl; }

  private:
    const int number;
};

template <size_t N>
class ManyObjects {
  public:
    ManyObjects(Object(&&objects)[N]) : objects(std::move(objects)) {}
    void print() {
        for (Object &object : objects)
            object.print();
    }

  private:
    Object objects[N];
};

template <size_t N> 
class ManyObjectsWrapper : public ManyObjects<N> {
  public:
    ManyObjectsWrapper(Object(&&objects)[N])
    : ManyObjects<N>(std::forward<Object[N]>(objects)) {}
};

int main() {
    ManyObjectsWrapper<3> wrapper = {{1, 2, 3}};
    wrapper.print();
}

输出

Object::Object(int)
Object::Object(int)
Object::Object(int)
Object::Object(Object&&)
Object::Object(Object&&)
Object::Object(Object&&)
The number is 1
The number is 2
The number is 3

它可以工作,而且输出看起来还不错,但我不确定我对右值引用和std::forwardstd::move 的使用是否正确。 尤其是ManyObjects::objects的初始化使用objects(std::move(objects))
有人可以对此发表评论吗?

编辑

一些说明:我将它与不支持 STL 的微控制器平台一起使用。我可以通过手动移植来移植像std::forward 这样的简单编译时构造。但是,我不能使用像 std::vector 这样的动态容器,因为它们使用堆,我必须在我使用的受限硬件上避免这种情况。
我希望将依赖项保持在最低限度,并且我不想浪费时间移植大部分 STL(例如std::array)。

主要目标是能够调用ManyObjectsWrapper&lt;3&gt; wrapper = {{1, 2, 3}};,其中参数始终是整数文字列表。

【问题讨论】:

  • 阅读this,它包含与他们合作所需的一切。
  • @Ron,我不明白你的意思,你能详细说明一下吗?简单地将std::move 替换为std::forward 不会编译。
  • 想知道为什么 gcc 编译 ManyObjects(Object(&amp;&amp;objects)[N]) : objects(std::move(objects)) {} C 数组是不可复制/移动的。
  • @Jarod42,确实,在那条线上当场哽咽,但 gcc(Ubuntu 5.4.0-6ubuntu1~16.04.10)编译并运行良好。有更好的方法的想法吗?
  • std::array&lt;Object, N&gt; 与 C 数组相反,是可复制/可移动的(但时间很短)。

标签: c++ move-semantics


【解决方案1】:

完美转发只对通用参考有意义,正如 Scott Meyers 所说的那样。使用通用引用,我们不知道我们会得到一个右值还是一个左值,因此我们不能简单地std::move 参数,因为它可能只是一个左值引用。

在您的情况下,不存在通用引用。您的模板参数只是一个数字 - 数组大小。要使右值引用成为通用引用,必须进行类型推导(通过模板或自动) - 在您的示例中并非如此,因此您的引用只是简单的右值引用。

我说得很简单,有关通用引用的更多(和更准确)信息请参阅this Scott Meyers article

【讨论】:

  • 我想我还有一些阅读要做,我目前正在通过Passer By提供的link工作,我会阅读你的文章。你能推荐一种使用ManyObjectsWrapper&lt;3&gt; wrapper = {{1, 2, 3}}; 之类的语法来初始化ManyObjects::objects 数组的方法吗?参数将始终是一个用大括号括起来的 int 字面量列表。
  • 对于您描述的大括号初始化,您可以使用初始化列表。例如,您可以有一个构造函数,它接受std::initializer_list&lt;Object&gt; 类型的参数,然后将列表的内容复制到您的数组。此外,如果您尝试使用现代 C++,请考虑使用 std::array&lt;int&gt;
  • 我正在使用的微控制器平台不支持初始化器列表。第三方提供了一些实现,但如果可能的话,我希望将依赖关系保持在最低限度。有替代品吗?
  • 我不知道,抱歉。顺便说一句,由于在您的示例中,您只有整数和静态数组,因此不能在任何地方进行任何移动,因为需要复制整数,并且可以指向数组,但最后,它们需要也要被复制。当您使用的类型具有移动 ctor 和/或运算符,并且这些运算符可以移动事物(通常是动态分配的内存或其他资源等)时,就会发生移动。
  • 我想我现在要忘记移动构造函数了。目前,我可以避免动态分配资源。但是,初始化数组(即使使用复制 ctor)仍然是一个问题。感谢您的帮助,那篇文章一定会在我提高 C++ 知识时派上用场。
猜你喜欢
  • 2012-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 2017-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多