Implementing boost::optional in c++11
Asked Answered
I

2

8

I am experimenting with implementing boost::optional like data structure using c++11 features. Here is what I have so far :

template<typename T>
struct maybe {
  bool valid;

  union {
    T value;
  };

  maybe() : valid(false) {}
  maybe(const T& _v) {
  valid = true;
    new (&value) T(_v);
  }
  maybe(const maybe& other) {
    if (other.valid) {
      valid = true;
      new (&value) T(other.value);
    }
    else valid = false;
  }

  ~maybe() {
     if (valid)
       value.~T();
  }

  bool is_valid() { return valid; }

  operator T&() {
    if (valid) return value;
    throw std::bad_exception();
  }
};

I make use of the unrestricted union feature to create a properly aligned space for the optional value that can be stored in-situ, instead of dynamically allocation space. Things work mostly, except when I want to create a maybe<> with a reference. For instance maybe<int&> causes g++ 4.7 to complain :

error: ‘maybe<int&>::<anonymous union>::value’ may not have reference type ‘int&’
because it is a member of a union

What should I do to make the maybe class store references? Any other improvements/suggestions to the class are also welcome.

Infrequency answered 9/8, 2012 at 0:12 Comment(0)
N
8

To make this work with references you definitely need an explicit specialization, because you can't do placement new of a reference: you need to use pointers for storage.

Beyond that, the code is missing a copy assignment operator. A move constructor, and move assignment operator would also be nice (especially since that's the #1 reason to reimplement boost::optional: the one in boost is lacking them).

Neutretto answered 9/8, 2012 at 0:22 Comment(9)
"That makes your class not working with non-default constructible types" This is not true, though. The standard seems to indicate that automatic initialization and destruction are suppressed for members of unions.Infrequency
BTW, alignas(T) char data[sizeof(T)] seems more C++11-ish than what you have.Infrequency
@Infrequency it's probably how aligned_storage is implemented. I find aligned_storage a lot more explicit about its business.Neutretto
@rmartinho To each, his own. The committee painstakingly moved the aligned_storage idiom to the language proper, so I don't feel like using a library wrapper over the language feature.Infrequency
@Infrequency regarding the default constructibility, that's quite interesting. Could you please refer me to the appropriate standard sections?Neutretto
Btw, I didn't notice before, but the copy constructor reads valid before it is initialised. It looks a lot like a copy assignment operator instead. Was that a transcription error or something?Neutretto
n2544.pdf - 3rd page 3rd paragraph.Infrequency
@rmartinho It was a bug in the copy constructor. Edited it out.Infrequency
let us continue this discussion in chatInfrequency
S
4

The optional types were proposed for C++14 but due to some corner cases around behavior undefined in standards it got postponed to C++17.

Fortunately, the UB issue shouldn't matter to most people because all major compilers do define it correctly. So unless you are using old compilers, you can actually just drop in the code available to implement optional type in your project (it's just one header file):

https://raw.githubusercontent.com/akrzemi1/Optional/master/optional.hpp

Then you can use it like this:

#if (defined __cplusplus) && (__cplusplus >= 201700L)
#include <optional>
#else
#include "optional.hpp"
#endif

#include <iostream>

#if (defined __cplusplus) && (__cplusplus >= 201700L)
using std::optional;
#else
using std::experimental::optional;
#endif

int main()
{
    optional<int> o1,      // empty
                  o2 = 1,  // init from rvalue
                  o3 = o2; // copy-constructor

    if (!o1) {
        cout << "o1 has no value";
    } 

    std::cout << *o2 << ' ' << *o3 << ' ' << *o4 << '\n';
}
Sulfamerazine answered 3/6, 2016 at 22:58 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.