19

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
KiYugadgeter
  • 3,796
  • 7
  • 34
  • 74
  • 2
    Compiles without issues by gcc 6.3.1. Most likely insufficient level of C++1z support by your compiler. – Sam Varshavchik May 27 '17 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 May 27 '17 at 13:32
  • @Walter I added error message. – KiYugadgeter May 29 '17 at 11:30

1 Answers1

25

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;
}
Walter
  • 44,150
  • 20
  • 113
  • 196
  • 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. – Greg Parker Oct 16 '17 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 Oct 18 '17 at 00:55