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"。好吧,我试过了,他们没有撒谎。 :(
似乎 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
, ifbool(*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"。好吧,我试过了,他们没有撒谎。 :(