首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++17表达式求值顺序和std::move

C++17表达式求值顺序和std::move
EN

Stack Overflow用户
提问于 2016-11-15 19:56:01
回答 2查看 761关注 0票数 13

在今天重构一些代码以更改到std::unique_ptr的原始指针时,由于http://en.cppreference.com/w/cpp/language/eval_order错误,我遇到了一个分段错误。

旧代码执行了如下操作:

代码语言:javascript
复制
void add(const std::string& name, Foo* f)
{
    _foo_map[name] = f;
}

void process(Foo* f)
{
    add(f->name, f);
}

首先,天真地重构代码以使用std::unique_ptr

代码语言:javascript
复制
void add(const std::string& name, std::unique_ptr<Foo> f)
{
    _foo_map[name] = std::move(f);
}

void process(std::unique_ptr<Foo> f)
{
    add(f->name, std::move(f)); // segmentation-fault on f->name
}

重构代码会导致分段错误,因为第二个参数(std::move(f))首先被处理,然后第一个参数(f->name)从变量boom中删除一个移出的变量!

可能的修复方法是在调用Foo::name之前获得add上的句柄。

代码语言:javascript
复制
void process(std::unique_ptr<Foo> f)
{
    const std::string& name = f->name;
    add(name, std::move(f));
}

或者也许:

代码语言:javascript
复制
void process(std::unique_ptr<Foo> f)
{
    Foo* fp = f.get();
    add(fp->name, std::move(f));
}

这两种解决方案都需要额外的代码行,而且似乎不像对add的原始调用(尽管是UB)那样可组合。

问题:

  • 以上提出的2种解决方案中有一种是惯用的C++,如果不是,是否有更好的替代方案?
  • 我看到C++17因P0145R3 - Refining Expression Evaluation Order for Idiomatic C++而发生了变化。这会改变上述解决方案中的任何一个/防止它们的缺陷吗?
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-11-15 20:24:57

对我来说,这两个提议看起来很糟糕。在任何一种情况下,您都要通过移动将您的Foo对象分发出去。这意味着在那之后,你不能再对它的状态做出任何假设。在处理第一个参数(对字符串或指向对象的指针的引用)之前,可以在add函数中释放它。是的,它在当前的实现中可以工作,但是一旦任何人接触到add的实现或更深层次的实现,它就会崩溃。

安全的方法:

  • 复制字符串并将其作为第一个参数传递给add
  • 重构您的add方法,它只接受一个类型为Foo的参数,并在该方法中提取Foo::name,而不将其作为参数。但是,在add方法中仍然存在相同的问题。

在第二种方法中,您应该能够绕过计算顺序问题,首先创建一个新的映射条目(带有默认值),然后获得一个可变的引用,然后分配值:

代码语言:javascript
复制
auto& entry = _foo_map[f->name];
entry = std::move(f);

不确定您的map实现是否支持获取对条目的可变引用,但对许多人来说,它应该可以工作。

如果我再想一想的话,你也可以采用“复制名字”的方法。无论如何,地图键都需要复制它。如果您手动复制它,您可以移动它,因为键没有开销。

代码语言:javascript
复制
std::string name = f->name;
_foo_map[std::move(name)] = std::move(f);

编辑:

正如注释中指出的那样,应该可以在_foo_map[f->name] = std::move(f)函数中直接分配add,因为这里保证了计算顺序。

票数 6
EN

Stack Overflow用户

发布于 2016-11-16 08:20:32

您可以让add引用f,从而避免不必要的复制/移动(相同的复制/移动会破坏f->name):

代码语言:javascript
复制
void add(const std::string& name, std::unique_ptr<Foo> && f)
{
    _foo_map[name] = std::move(f);
}

void process(std::unique_ptr<Foo> f)
{
    add(f->name, std::move(f));
}

必须在调用foo_map[name]之前对operator=进行评估,因此即使name引用依赖于f的内容,也没有问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40618581

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档