Clang 无法与 std::experimental::optional 一起正常工作

Clang does not work properly with std::experimental::optional

似乎 clang 不能与 std::experimental::optional 一起正常工作。
考虑以下示例:

#include <experimental/optional>
#include <iostream>

struct Foo { int bar; };

int main() {
    Foo foo;
    std::experimental::optional<Foo> opt = foo;
    opt.value().bar = 42;
    std::cout << opt.value().bar << std::endl;
}

它在 g++ 5.3.1 版中编译良好,但在 clang 7.0.0 版和 clang 7.0.2 版中均无法编译。
返回的错误是:

Undefined symbols for architecture x86_64:
"std::experimental::bad_optional_access::~bad_optional_access()", referenced from:
    _main in main-11b2dd.o
"typeinfo for std::experimental::bad_optional_access", referenced from:
    _main in main-11b2dd.o
"vtable for std::experimental::bad_optional_access", referenced from:
    std::experimental::bad_optional_access::bad_optional_access() in main-11b2dd.o
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我没能在 clang 的错误报告中找到任何打开的问题。
谁的行为正确?我猜 g++ 工作正常,而 clang 似乎有问题。我错了吗?

EDIT1

实际上,错误似乎是由于 bad_optional_access 的定义引起的,即使问题是在使用 clang 时发生的。

EDIT2

没有命令行参数,但是-std=c++14
在 osx 上使用 clang 执行的测试,只要您不使用 value 成员方法,它就可以正常编译(因此 optional 是免费可用的)。
这意味着它使用以下方式编译和链接:

opt->bar

而不是:

opt.value().bar

您的代码看起来非常好,opt.value() 应该 return 对包含的值的引用,假设它已被使用或抛出异常。我们可以看一个older proposals,它比后面的例子多,它包括以下段落:

Using the indirection operator for a disengaged object is an undefined behavior. This behavior offers maximum runtime performance. In addition to indirection operator, we provide member function value that returns a reference to to the contained value if one exists or throws an exception (derived from logic_error) otherwise

如果查看 latest proposal,它会将 value() 描述为:

constexpr T const& optional<T>::value() const;
T& optional<T>::value();

Returns:

*val, if bool(*this).

Throws:

bad_optional_access if !*this.

Remarks:

The first function shall be a constexpr function.

并提供了 value:

的示例实现
constexpr T const& value()
{
    return *this ? storage_.value_ : (throw bad_optional_access(""), storage_.value_);
}

在您的情况下,opt 已参与,如果不是,则应该抛出,我们不会调用未定义的行为。

注意,这是在 Wandbox 上最后几个版本的 clang 上编译的(see it live)。

正如 Petesh 所说,这可能是您的平台故意为之,但确认这一点的唯一方法是提交错误报告。

另请注意,正如提案所述,还有一个 reference implementation on github 可能是短期选择。

TL;DR:

@Petesh 在评论中的解决方案为我解决了这个问题(发生在 OSX 上从源代码构建的 Clang 4.0.0),即在 .. ./experimental/optional: std::experimental::bad_optional_access::~bad_optional_access‌​() _NOEXCEPT = default;.

详情:

按照@Shafik 的回答使用取消引用运算符没有按预期工作,因为当前的实现提供 (void)0 作为取消引用脱离的可选值时的值。例如,这会为可选生成 0——这是不正确的行为。 (我也不认为(dis)参与检查应该作为断言来实现,因为断言很可能在发布版本中被编译出来,这是出乎意料的。)

要解决 (void)0 问题,_LIBCPP_ASSERT 必须定义为正确的行为——如果 _LIBCPP_DEBUG_LEVEL 定义为至少 1,则 _LIBCPP_ASSERT 将被定义为 ((x) ? (void)0 : (_VSTD::fprintf(stderr, "%s\n", m), _VSTD::abort())),这应该会产生预期的行为。

但问题是,根据 libc++ docs_LIBCPP_DEBUG_LEVEL 是一项正在进行的工作,定义它会产生 "fairly nasty compile errors"。好吧,我试过了,他们没有撒谎。 :(