20

I am trying to use std::optional but my code raise error.
I have specified #include <experimental/optional> and compiler options are -std=c++1z, -lc++experimental.

How to use std::experimental::optional?

The following is code:

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

std::experimental::optional<int> my_div(int x, int y) {
    if (y != 0) {
        int b = x / y;
        return {b};
    }
    else {
        return {};
    }
}

int main() {
    auto res = my_div(6, 2);
    if (res) {
        int p = res.value();
        std::cout << p << std::endl;
    }
}

error message:

optional.cpp:17:21: error: call to unavailable member function 'value': 
        int p = res.value();
                ~~~~^~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/experimental/optional:525:17: note: candidate function has been explicitly made unavailable
    value_type& value()
                ^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/experimental/optional:517:33: note: candidate function has been explicitly made unavailable
    constexpr value_type const& value() const
                                ^
1 error generated.

OS: macOS 10.12.5

Compiler version:

Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
3
  • 2
    Compiles without issues by gcc 6.3.1. Most likely insufficient level of C++1z support by your compiler. Commented May 27, 2017 at 13:18
  • Sorry, my closing as dupe was premature. I turns out that (at least on my MAC book, also running the same compiler) there is a file /Library/Developer/CommandLineTools/usr/include/c++/v1/experimental/optional, so this should work ... But, unfortunately, you didn't show us the error message, so we may have to close this anyway .
    – Walter
    Commented May 27, 2017 at 13:32
  • @Walter I added error message. Commented May 29, 2017 at 11:30

1 Answer 1

27

Okay, after you posted your error, I could look into that (but you could have done exactly the same).

IN SHORT

This is a problem/bug with optional as provided by Apple on OSX, but there is an easy workaround.

WHAT'S GOING ON

The file /Library/Developer/CommandLineTools/usr/include/c++/v1/exper‌​imental/optional declares the offending function optional::value as

template <class _Tp>
class optional
    : private __optional_storage<_Tp>
{
  /* ... */

  _LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
  constexpr value_type const& value() const
  {
    if (!this->__engaged_)
        throw bad_optional_access();
    return this->__val_;
  }

  _LIBCPP_INLINE_VISIBILITY _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
  value_type& value()
  {
    if (!this->__engaged_)
        throw bad_optional_access();
    return this->__val_;
  }
  /* ... */
};

Running the preprocessor only (compiler option -E) reveals that the macros expand to

#define _LIBCPP_INLINE_VISIBILITY \
  __attribute__ ((__visibility__("hidden"), __always_inline__))
#define _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS \
  __attribute__((unavailable))

In particular, the macro _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS is #defined in file /Library/Developer/CommandLineTools/usr/include/c++/v1/__config as

// Define availability macros.
#if defined(_LIBCPP_USE_AVAILABILITY_APPLE)
// ...
#define _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS __attribute__((unavailable))
// ...
#else
// ...
#define _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS
// ...
#endif

Thus, this is a Apple specific change from LLVM's libc++ API. As the name of the macro implies, the reason is that Apple does not make

class bad_optional_access
: public std::logic_error
{
public:
  bad_optional_access() : std::logic_error("Bad optional Access") {}
  virtual ~bad_optional_access() noexcept;
};

available and hence cannot implement functionality (optional::value) that depends on it. Why bad_optional_access is not provided (thereby breaking the standard) is unclear, but it may have to do with the fact that a library (dylib) must be altered to contain bad_optional_access::~bad_optional_access().

HOW TO WORK AROUND

simply use optional::operator* instead

int p = *res;

The only real difference is that no access check is done. If you need that, do it yourself

template<typename T>
T& get_value(std::experimental::optional<T> &opt)
{
  if(!opt.has_value())
    throw std::logic_error("bad optional access");
  return *opt;
}

template<typename T>
T const& get_value(std::experimental::optional<T>const &opt)
{
  if(!opt.has_value())
    throw std::logic_error("bad optional access");
  return *opt;
}
2
  • 2
    "Why bad_optional_access is not provided (thereby breaking the standard) is unclear, but it may have to do with the fact that library (dylib) must be altered to contain bad_optional_access::~bad_optional_access()." That's right. Future OS versions will provide the necessary symbols, and then apps deploying to those versions or newer will be able to use it. Commented Oct 16, 2017 at 21:21
  • 3
    @GregParker Do you speak with any authority on this matter? Why was this designed in this way deliberately braking the standard?
    – Walter
    Commented Oct 18, 2017 at 0:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.