简体   繁体   中英

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

It seems that clang does not work properly with std::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.
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.
Who is behaving properly? I guess g++ works fine, while clang seems to be bugged. Am I wrong?

EDIT1

Actually, the bug seems to be due to the definition of bad_optional_access , even though the problem happens while using clang.

EDIT2

No command line parameters, but -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.
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. 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

and if look at the latest proposal it describes value() as:

 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.

and provides a sample implementation of 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.

Note, this compiles on the last several versions of clang on Wandbox( see it live ).

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.

Also note as the proposal notes there is also a reference implementation on github which may be a short-term option.

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; .

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. This yields a 0 for optional for example--which is incorrect behavior. (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.)

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.

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". Well, I tried it, and they did not lie. :(

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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