在调用函数的作用域中构造返回的对象

时间:2015-01-29 11:20:49

标签: c++ optimization rvo

是否可以强制C ++在调用函数的范围内构造一个对象?我的意思是明确地做返回值优化(RVO)所做的事情。

我有一些容器类,它们属于派生链。由于类是使用堆栈数据构造的,因此无法返回它们,因此我禁用了复制构造函数和赋值运算符。对于每个类,我提供一个迭代器。每个迭代器的构造函数只有一个参数:指向容器类的指针。要获取迭代器,我想使用此函数:

BindPackIterator BindPack.begin(void)
{
    return BindPackIterator(this);
}

在此背景下:

for (auto i=bindpack.begin(); !i.end(); ++i) { i.run(); }

编译器发出错误,抱怨无法复制BindPackIterator对象。记住,我禁用了它们。

我想要发生的是BindPackIterator在调用函数的范围内实例化,以避免复制或移动操作。

在这种特殊情况下,我知道我可以做一个解决方法,更改begin函数以返回BindPack指针,

for(BindPackIterator i=bindpack.begin(); !i.end(); ++i) { i.run(); }

我用decltype和这种结构进行了一些实验,但没有成功:

auto BindPack::begin(void) -> BindPackIterator
{
    return BindPackIterator(this);
}

这只是我目前感到沮丧的例子。还有其他项目明显的解决方案是函数在调用函数的范围中实例化一个对象。移动构造函数(foo&&)在某些情况下有帮助,但对于具有许多数据成员的对象,即使这样也可能效率低下。是否存在允许在调用者范围内进行对象构造/实例化的设计模式?

2 个答案:

答案 0 :(得分:1)

将n.m.的注释放入代码中,编写BindPackIterator的构造函数,该构造函数接受BindPack并在“begin”状态下初始化迭代器。 e.g:

BindPackIterator(BindPack* pack) : pack(pack), pos(0){ }

您可以在for循环中使用:

BindPack pack;

for(BindPackIterator i(&pack); !i.end(); ++i){
  i.run();
}

Live demo

答案 1 :(得分:0)

公平地说答案是“不”,在调用函数的范围内构造返回的对象是不可能的?或者换句话说,您无法明确告诉编译器使用RVO。

可以肯定的是,这是一种危险的可能性:用于构造对象的堆栈内存虽然在被调用函数中可用但在调用函数中无效,即使这些值在放弃的堆栈帧中可能保持不变。这将导致不可预测的行为。

经过进一步的考虑,在总结这个响应时,我意识到编译器可能无法准确预测在调用函数中创建并在被调用函数中初始化的对象所需的堆栈大小,它会如果执行已经传递给另一个函数,则无法动态扩展堆栈帧。这些考虑使我的整个想法变得不可能。

那就是说,我想解决解决迭代器示例的变通方法。

我不得不放弃使用auto这样的想法:

for (auto i=bindpack.begin(); !i.end(); ++i)

放弃了auto,并且意识到无论如何显式地命名变量更为明智(如果迭代器不同以至于需要一个新类,最好将其命名为避免混淆),我使用这个构造:

BindPackIterator(BindPack &ref) : m_ref_pack(ref), m_index(0) { }

为了能够写:

for (BindPackIterator i=bindpack; !i.end(); ++i)

更喜欢使用作业进行初始化。我曾经在1990年代末期大量使用C ++时曾经这样做过,但最近我并没有为我工作。由于上述原因,编译器会要求我不想定义的复制操作符。现在我认为这个问题是由于我定义的构造函数和赋值运算符集合以传递-Weffc ++测试。使用此示例的简化类允许它工作。

比迭代器更复杂的对象的另一种解决方法可能是使用元组作为需要多个变量初始化的对象的构造函数参数。可能有一个转换运算符,它从初始化对象的类中返回必要的元组。

构造函数可能如下所示:

FancyObject(BigHairyTuple val) : m_data1(get<0>(val)), m_data2(get<1>(val), etc

并且贡献对象将定义:

class Foo
{
    ...
    operator BigHairyTuple(void) {
        return BigHairyTuple(val1, val2, ...);
    }
};

允许:

FancyObject fo = foo;

我没有测试过这个具体的例子,但是我正在使用类似的东西,它似乎可以工作,有一些可能的小改进。