boost::optional 奇怪的未初始化问题

use*_*932 5 c++ gcc boost c++11 boost-optional

gcc 时不时地通过 may-uninitialized 发出有关 boost::optional 工作的警告,但我认为这是误报(如此处描述的https://github.com/boostorg/optional/issues/72)。

但是现在valgrind在运行时报告了同样的问题,所以出了点问题,不知道是什么问题:

//Foo.hpp
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <cstdio>
#include <string>

extern "C" {
typedef struct FooOpaque FooOpaque;

FooOpaque *Foo_new();

void Foo_delete(const FooOpaque *self);
}

class Foo {
public:
  explicit Foo(FooOpaque *o) noexcept : self_(o) {}
  Foo(const Foo &) = delete;
  Foo &operator=(const Foo &) = delete;

  Foo(Foo &&o) noexcept : self_(o.self_) { o.self_ = nullptr; }
  Foo &operator=(Foo &&o) noexcept {

    assert(this != &o);
    free_mem(this->self_);
    self_ = o.self_;
    o.self_ = nullptr;
    return *this;
  }
  ~Foo() noexcept { free_mem(this->self_); }

private:
  static void free_mem(FooOpaque *&p) noexcept {
    //    printf("test\n");
    if (p != nullptr) {
      Foo_delete(p);
    }
    p = nullptr;
  }

  FooOpaque *self_ = nullptr;
};

boost::variant<boost::optional<Foo>, std::string> f_res_opt(int var);
Run Code Online (Sandbox Code Playgroud)
//Foo.cpp
#include <cassert>
#include <cstdint>
#include <cstring>

#include "Foo.hpp"

extern "C" {
struct FooOpaque {
  int data;
};

FooOpaque *Foo_new() {
  printf("Foo_new begin\n");
  auto p = new FooOpaque;
  p->data = 17;
  return p;
}

void Foo_delete(const FooOpaque *self) {
  assert(self != nullptr);
  printf("Foo_new delete\n");
  delete self;
}
}

boost::variant<boost::optional<Foo>, std::string> f_res_opt(int var) {
  switch (var) {
  case 0:
    return {boost::optional<Foo>{Foo{Foo_new()}}};
  case 1:
    return {boost::optional<Foo>{boost::none}};
  case 2:
    return {std::string{}};
  default:
    std::abort();
  }
}
Run Code Online (Sandbox Code Playgroud)
// main.cpp
#include "Foo.hpp"

int main() {

  auto res1 = f_res_opt(0);
  auto res1_ok = boost::get<boost::optional<Foo>>(boost::move(res1));

  printf("step 2\n");

  auto res2 = f_res_opt(1);

  auto res2_ok = boost::get<boost::optional<Foo>>(boost::move(res2));

  printf("step 3\n");

  auto res3 = f_res_opt(2);

  auto res3_ok = boost::get<std::string>(boost::move(res3));
}
Run Code Online (Sandbox Code Playgroud)

valgrind 和 gcc 在线报告问题:

auto res2_ok = boost::get<boost::optional<Foo>>(boost::move(res2));
Run Code Online (Sandbox Code Playgroud)

那个潜在的Foo::free_mem可以用于单元化内存。

奇怪的是,如果我在Foo::free_mem gcc 警告 (9.1.0) 中启用 printf消失并且 valgrind (3.15) 不报告任何问题,如果我将其评论回 gcc 并且 valgrind 报告问题。

我以这种方式用 gcc 编译示例:

//Foo.hpp
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <cstdio>
#include <string>

extern "C" {
typedef struct FooOpaque FooOpaque;

FooOpaque *Foo_new();

void Foo_delete(const FooOpaque *self);
}

class Foo {
public:
  explicit Foo(FooOpaque *o) noexcept : self_(o) {}
  Foo(const Foo &) = delete;
  Foo &operator=(const Foo &) = delete;

  Foo(Foo &&o) noexcept : self_(o.self_) { o.self_ = nullptr; }
  Foo &operator=(Foo &&o) noexcept {

    assert(this != &o);
    free_mem(this->self_);
    self_ = o.self_;
    o.self_ = nullptr;
    return *this;
  }
  ~Foo() noexcept { free_mem(this->self_); }

private:
  static void free_mem(FooOpaque *&p) noexcept {
    //    printf("test\n");
    if (p != nullptr) {
      Foo_delete(p);
    }
    p = nullptr;
  }

  FooOpaque *self_ = nullptr;
};

boost::variant<boost::optional<Foo>, std::string> f_res_opt(int var);
Run Code Online (Sandbox Code Playgroud)

那么发生了什么,我的代码错误,boost::optional 错误,或者 valgrind 和 g++ 中的同时错误?

可能是 g++ 中的这个错误会生成错误的代码,因此 valgrind 也会抱怨错误?