Как использовать std::Optional в С++?

Я пытаюсь использовать std::Optional, но мой код вызывает ошибку.
Я указал #include <experimental/optional>, а параметры компилятора: -std=c++1z, -lc++experimental.

Как использовать std::experimental::optional?

Ниже приведен код:

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

сообщение об ошибке:

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.

ОС: macOS 10.12.5

Версия компилятора:

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

person KiYugadgeter    schedule 27.05.2017    source источник
comment
Компилируется без проблем gcc 6.3.1. Скорее всего недостаточный уровень поддержки C++1z вашим компилятором.   -  person Sam Varshavchik    schedule 27.05.2017
comment
Извините, мое закрытие как обмана было преждевременным. Оказывается, (по крайней мере, в моей книге MAC, также работающей с тем же компилятором) есть файл /Library/Developer/CommandLineTools/usr/include/c++/v1/experimental/optional, так что это должно работать ... Но, к сожалению, вы не показали нам сообщение об ошибке, поэтому нам, возможно, придется все равно закрой это.   -  person Walter    schedule 27.05.2017
comment
@Walter, я добавил сообщение об ошибке.   -  person KiYugadgeter    schedule 29.05.2017


Ответы (1)


Хорошо, после того, как вы опубликовали свою ошибку, я мог бы ее изучить (но вы могли бы сделать то же самое).

КОРОТКО

Это проблема/ошибка с optional, предоставленная Apple для OSX, но есть простой обходной путь.

ЧТО ПРОИСХОДИТ

Файл /Library/Developer/CommandLineTools/usr/include/c++/v1/exper‌​imental/optional объявляет оскорбительную функцию optional::value как

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_;
  }
  /* ... */
};

Запуск только препроцессора (опция компилятора -E) показывает, что макросы расширяются до

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

В частности, макрос _LIBCPP_AVAILABILITY_BAD_OPTIONAL_ACCESS есть #defined в файле /Library/Developer/CommandLineTools/usr/include/c++/v1/__config как

// 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

Таким образом, это специфическое изменение Apple по сравнению с API LLVM libc++. Как следует из названия макроса, причина в том, что Apple не делает

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

доступен и, следовательно, не может реализовать зависящую от него функциональность (optional::value). Почему не указан bad_optional_access (тем самым нарушая стандарт), неясно, но это может быть связано с тем, что библиотека (dylib) должна быть изменена, чтобы содержать bad_optional_access::~bad_optional_access().

КАК ОБРАЩАТЬСЯ

просто используйте optional::operator* вместо этого

int p = *res;

Единственная реальная разница в том, что проверка доступа не выполняется. Если вам это нужно, сделайте это сами

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;
}
person Walter    schedule 29.05.2017
comment
Почему не предоставляется bad_Optional_access (тем самым нарушая стандарт), неясно, но это может быть связано с тем, что библиотека (dylib) должна быть изменена, чтобы содержать bad_Optional_access::~bad_Optional_access(). Вот так. В будущих версиях ОС будут предоставлены необходимые символы, и тогда приложения, развертываемые в этих версиях или более новых, смогут их использовать. - person Greg Parker; 17.10.2017
comment
@GregParker Вы говорите с какими-либо авторитетами по этому вопросу? Почему это было разработано таким образом, преднамеренно нарушая стандарт? - person Walter; 18.10.2017