简体   繁体   English

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

[英]Clang does not work properly with std::experimental::optional

It seems that clang does not work properly with std::experimental::optional . 似乎clangstd::experimental::optional不能正常工作。
Consider the following example: 请考虑以下示例:

#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;
}

It compiles fine with g++ version 5.3.1, but it doesn't neither with clang version 7.0.0 nor with clang version 7.0.2. 它与g ++版本5.3.1编译良好,但它既没有clang版本7.0.0也没有clang版本7.0.2。
The returned error is: 返回的错误是:

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)

I didn't manage to find any issue opened in the bug report for clang. 我没有设法在clang的bug报告中找到任何问题。
Who is behaving properly? 谁表现得很好? I guess g++ works fine, while clang seems to be bugged. 我猜g ++工作得很好,而clang似乎被窃听了。 Am I wrong? 我错了吗?

EDIT1 EDIT1

Actually, the bug seems to be due to the definition of bad_optional_access , even though the problem happens while using clang. 实际上,这个bug似乎是由于bad_optional_access的定义,即使在使用clang时问题发生了。

EDIT2 EDIT2

No command line parameters, but -std=c++14 . 没有命令行参数,但是-std=c++14
Tests performed with clang on osx, it compiles fine (so optional is freely available) as far as you don't use the value member method. 在osx上使用clang进行测试,只要你不使用value成员方法,它就可以很好optional编译(所以optional是免费的)。
That means that it compiles and links using: 这意味着它使用以下方式编译和链接:

opt->bar

Instead of: 代替:

opt.value().bar

Your code looks perfectly fine, opt.value() should return a reference to the contained value assuming it is engaged or throw an exception. 您的代码看起来非常好, opt.value()应该返回对包含值的引用,假设它已被使用或抛出异常。 We can look at one of the older proposals which has more examples than the later ones and it includes the following paragraph: 我们可以看一个较旧的提案 ,其中包含比后者更多的例子,它包括以下段落:

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 除了间接运算符之外, 我们还提供成员函数值,该值返回对包含的值的引用(如果存在)或抛出异常(从logic_error派生),否则

and if look at the latest proposal it describes value() as: 如果查看最新的提案,它将value()描述为:

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

Returns: 返回:

*val , if bool(*this) . *val ,如果bool(*this)

Throws: 抛出:

bad_optional_access if !*this . bad_optional_access if !*this

Remarks: 备注:

The first function shall be a constexpr function. 第一个函数应该是constexpr函数。

and provides a sample implementation of value : 并提供了value的示例实现:

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

In your case opt is engaged and if it was not it should just throw, we would not be invoking undefined behavior. 在你的情况下, opt是参与的,如果它不是它应该只是抛出,我们不会调用未定义的行为。

Note, this compiles on the last several versions of clang on Wandbox( see it live ). 注意,这会编译Wandbox上最后几个版本的clang( 请参见实时 )。

So as Petesh suggests this could be intentional on your platform but the only way to confirm that would be to file a bug report. 因此,Petesh建议这可能是您的平台上的故意,但确认这将是提交错误报告的唯一方法。

Also note as the proposal notes there is also a reference implementation on github which may be a short-term option. 另请注意,提案注意到github上也有一个参考实现 ,可能是一个短期选项。

TL;DR: TL; DR:

@Petesh's solution in the comments solved the issue for me (occurred with Clang 4.0.0 built from source on OSX), namely to add a default implementation ( = default ) in .../experimental/optional: std::experimental::bad_optional_access::~bad_optional_access‌​() _NOEXCEPT = default; @ Petesh在评论中的解决方案解决了我的问题(发生在OSX上的源代码中构建的Clang 4.0.0),即在... / experimental / optional中添加默认实现( = default ): std::experimental::bad_optional_access::~bad_optional_access‌​() _NOEXCEPT = default; .

Details: 细节:

Using the dereference operator as per @Shafik's answer did not work as expected, because the current implementation supplies (void)0 as a value when a disengaged optional is dereferenced. 根据@ Shafik的回答使用解引用运算符不能按预期工作,因为当取消引用脱离的可选项时,当前实现提供(void)0作为值。 This yields a 0 for optional for example--which is incorrect behavior. 例如,这为可选项产生0 - 这是不正确的行为。 (I also don't feel that the (dis)engagement check should be implemented as an assert, because the assert is likely to be compiled out in release builds, which is unexpected.) (我也不认为(dis)参与检查应该作为断言实现,因为断言很可能是在发布版本中编译出来的,这是意料之外的。)

To fix the (void)0 problem, _LIBCPP_ASSERT must be defined with proper behavior--if _LIBCPP_DEBUG_LEVEL to be defined to be at least 1, then _LIBCPP_ASSERT will be defined as ((x) ? (void)0 : (_VSTD::fprintf(stderr, "%s\\n", m), _VSTD::abort())) , which should yield expected behavior. 以固定(void)0问题, _LIBCPP_ASSERT必须用正确的行为被定义-如果_LIBCPP_DEBUG_LEVEL被定义为至少1,则_LIBCPP_ASSERT将被定义为((x) ? (void)0 : (_VSTD::fprintf(stderr, "%s\\n", m), _VSTD::abort())) ,它应该产生预期的行为。

But the problem is, according to the libc++ docs , _LIBCPP_DEBUG_LEVEL is a work in progress, and defining it will yield "fairly nasty compile errors". 但问题是,根据libc ++文档_LIBCPP_DEBUG_LEVEL是一项正在进行的工作,定义它将产生“相当讨厌的编译错误”。 Well, I tried it, and they did not lie. 好吧,我试过了,他们没有说谎。 :( :(

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM