std::future<neither::Either<int, std::string>> segmentation fault

4

这个问题中的代码使用了在https://github.com/LoopPerfect/neither找到的Either<>实现。明确一下,我怀疑这不是这个特定库的问题,否则我会在那里创建一个issue。

下面的代码片段按预期工作:

std::future<std::string> f = std::async(std::launch::async, []()
{
    return "test";
}

std::cout << f.get() << std::endl;

以下代码会产生分段错误:
std::future<neither::Either<int, std::string>> f = std::async(std::launch::async, []()
{
    return neither::Either<int, std::string>(neither::right(std::string("test")));
});

neither::Either<int, std::string> v = f.get(); // Segmentation fault
std::cout << v.right().value << std::endl;

使用left(-1)neither::Either<int, int>left()right()都可以起作用。我知道如果你多次调用std::future::get可能会导致段错误,在这种情况下,在调用get之前std::future::valid将返回false,但是valid会返回true。

这里有什么我不明白的吗?

1个回答

11

我是不是漏掉了什么?

这个库没有正确实现。特别针对这个问题,拷贝构造函数有问题:

constexpr Either( Either<L, R> const& e )
  : isLeft(e.isLeft) {
  if(isLeft) {
    leftValue = e.leftValue;
  } else {
    rightValue = e.rightValue; // (*)
  }
}

那里无法为 this->rightValue 赋值,因为该位置上不存在 std::string,我们有未初始化的内存。

一个正确的拷贝构造函数应该是:

Either(Either<L, R> const& e)
  : isLeft(e.isLeft)
{
  if(isLeft) {
    new (&leftValue) L(e.leftValue);
  } else {
    new (&rightValue) R(e.rightValue);
  }
}

或者,既然我们正在编写可供各种邪恶类型使用的通用库代码,您会想要:

::new (static_cast<void*>(std::addressof(leftValue))) L(e.leftValue);

@DavidFreitag e 构造正确,但 this 不是。星号行调用了 std::string::operator=(std::string const&),它需要存在一个 std::string - 但我们没有一个 std::string,我们只有未初始化的内存。 - Barry
@DavidFreitag 你不是默认构造,而是复制构造。例如 new (&rightValue) R(e.rightValue) - Barry
@T.C. 为什么特别使用 static_cast<void*> - Barry
因为 ::operator new(size_t, int*) 等函数并没有被保留? - T.C.
3
@Gaetano struct L { void operator&(); }; 可以翻译为:定义了一个名为L的结构体,其中包含一个名为operator&的函数。 - Barry
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接