3

是否可以强制 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&&) 会有所帮助,但对于具有许多数据成员的对象,即使这样也可能效率低下。是否有允许在调用者范围内构造/实例化对象的设计模式?

4

2 回答 2

1

将 nm 的注释放入代码中,为其编写一个构造函数BindPackIteratorBindPack并将迭代器初始化为“开始”状态。例如:

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

您可以在 for 循环中使用:

BindPack pack;

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

Live demo

于 2015-01-31T00:11:53.913 回答
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;

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

于 2015-01-31T12:26:37.237 回答