【问题标题】:Filling a tuple using a factory and avoiding copying使用工厂填充元组并避免复制
【发布时间】:2013-07-30 13:16:13
【问题描述】:

我正在尝试编写一个 (C++98) 程序,其中出现以下模式:我有一个非常简单的通用元组类,我需要用使用工厂构造的值填充它。最小示例代码如下:

#include <iostream>

class diagnostics
{
private:
  int payload;
public:
  diagnostics(int a)
    : payload(a)
  {
    std::cout << "constructor\n";
  }

  diagnostics(const diagnostics& o)
    : payload(o.payload)
  {
    std::cout << "copy constructor\n";
  }

  ~diagnostics()
  {
    std::cout << "destructor [" << payload << "]\n";
  }
};

struct creator
{
  static diagnostics create()
  {
    static int i = 0;
    return diagnostics(++i);
  }
};

template<class Head, class Tail>
struct tuple
{
  Head head;
  Tail tail;
  typedef Head head_t;
  typedef Tail tail_t;

  tuple(const Head& h, const Tail& t)
    : head(h), tail(t)
  {
  }
};
struct empty_tuple { };

template<class Tuple, class Create>
struct create_helper
{
  static Tuple create()
  {
    return Tuple(Create::create(),
        create_helper<typename Tuple::tail_t, Create>::create());
  }
};
template<class Create>
struct create_helper<empty_tuple, Create>
{
  static empty_tuple create()
  {
    return empty_tuple();
  }
};
template<class Tuple, class Create>
Tuple create()
{
  //return Tuple(Create::create(), empty_tuple()); //(*)
  return create_helper<Tuple, Create>::create();
}

int main()
{
  typedef tuple<diagnostics, empty_tuple> tuple_t;
  tuple_t a = create<tuple_t, creator>();
}

输出是

constructor
copy constructor
destructor [1]
destructor [1]

我想去掉中间的两行。

为了简化讨论,我们可以在上面的代码中取消注释标记为 (*) 的行;这会破坏通用性,但不会破坏程序。

现在是我的主要问题:我该如何解决这种情况?标准中是否有阻止 RVO 的内容(大概 RVO 必须在这里递归完成)?如果不是,接受编译器不够好,有没有办法让我以明确的方式发生这种情况?我可以使 create() 的调用端复杂化,但我不想使元组中的对象复杂化(特别是,其中一些对象不能默认构造,并且我不想引入额外的“未初始化”向他们说明)。 emplace new 可能有帮助吗?

以下问题似乎相关,但最终没有帮助:Why does not RVO happen here?)

【问题讨论】:

  • 我认为这行 return create_helper&lt;Tuple, Create&gt;::create(); 正在您的输出中创建额外的两行。该行返回一个创建的对象,然后tuple_t a = create&lt;tuple_t, creator&gt;(); 使用复制构造函数复制到a,然后create&lt;...&gt;() 的返回值被破坏。
  • 编译器看到您的诊断类具有非平凡的构造函数/析构函数(具有不少副作用)并因此拒绝 RVO 似乎并非不合理。请注意,该标准特别允许在复制初始化的情况下进行优化 (diagnostics instance = diagnostics(...)) IIRC
  • 最小示例不会生成显示的输出,因为没有人在其中调用creator。请添加一个main 测试工具,它将实际生成显示的输出(理想情况下作为高优化级别)。
  • @Yakk:你能详细说明一下吗?我不仅得到了我声称的输出,而且我认为它来自 abiessu 指出的行。
  • @TomBachmann 简单解释:我没有向下滚动。 :)

标签: c++ nrvo


【解决方案1】:

我觉得在五分钟后回答我自己的问题有点愚蠢,但这似乎是获取以下信息的最合适的方式:

这是一种解决方案。我们可以将创建代码转移到元组类本身:

struct FILLIT { };

template<class Head, class Tail>
struct tuple
{
  Head head;
  Tail tail;
  typedef Head head_t;
  typedef Tail tail_t;

  tuple(const Head& h, const Tail& t)
    : head(h), tail(t)
  {
  }

  tuple(const tuple& o)
    : head(o.head), tail(o.tail)
  {
    std::cout << "tuple copy\n";
  }

  template<class Create>
  tuple(FILLIT, Create c)
      : head(Create::create()), tail(FILLIT(), c)
  {
  }
};
struct empty_tuple
{
  empty_tuple() {};
  template<class C>
  empty_tuple(FILLIT, C) {};
};

create 的兼容实现是

template<class Tuple, class Create>
Tuple create()
{
  return Tuple(FILLIT(), Create());
}

我仍然对其他方法感兴趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 2016-07-11
    • 2022-11-04
    • 2014-12-24
    • 2015-04-05
    • 1970-01-01
    相关资源
    最近更新 更多