patterncppMinor
Implementation similar to boost::optional for modelling function failure
Viewed 0 times
functionoptionalforfailureimplementationsimilarboostmodelling
Problem
The idea being you can have a simple type called
```
#ifndef MAYBE_H_
#define MAYBE_H_
#include
namespace {
// implementation enabled for non-reference types
template
struct maybe {
maybe() : valid(false) {} // default constructor
maybe(const T& val) : valid(true) { // contructor from wrapped value
new ((void*)storage) T(val);
}
maybe(const maybe &other) { // copy constructor
valid = other.valid;
if (valid) {
new ((void*)storage) T(other.get_ref());
}
}
maybe(maybe &&other) : maybe() { // move constructor
std::swap(valid, other.valid);
std::swap(storage, other.storage);
}
~maybe() { // destructor
if (valid) {
get_ref().~T();
}
}
// assignment operator, uses copy-and-swap trick
maybe& operator =(maybe other) {
swap(*this, other);
return *this;
}
// type-coercion operator to get back to wrapped value
// calling without checking validity causes bad_exception
operator T&() {
if (!valid) {
throw std::bad_exception();
}
return get_ref();
}
// for checking validity of container
explicit operator bool() { return valid; }
private:
bool valid;
alignas(T) char storage[sizeof(T)];
T& get_ref() const { return static_cast((void)storage); }
};
// implementation enabled
maybe that wraps another type, which provides the ability to possibly not return a value from a function. I'm sure it's not perfect yet, but it's got the broad strokes I'm looking for. I'm mostly looking for corner cases I've missed and anything else I've overlooked.```
#ifndef MAYBE_H_
#define MAYBE_H_
#include
namespace {
// implementation enabled for non-reference types
template
struct maybe {
maybe() : valid(false) {} // default constructor
maybe(const T& val) : valid(true) { // contructor from wrapped value
new ((void*)storage) T(val);
}
maybe(const maybe &other) { // copy constructor
valid = other.valid;
if (valid) {
new ((void*)storage) T(other.get_ref());
}
}
maybe(maybe &&other) : maybe() { // move constructor
std::swap(valid, other.valid);
std::swap(storage, other.storage);
}
~maybe() { // destructor
if (valid) {
get_ref().~T();
}
}
// assignment operator, uses copy-and-swap trick
maybe& operator =(maybe other) {
swap(*this, other);
return *this;
}
// type-coercion operator to get back to wrapped value
// calling without checking validity causes bad_exception
operator T&() {
if (!valid) {
throw std::bad_exception();
}
return get_ref();
}
// for checking validity of container
explicit operator bool() { return valid; }
private:
bool valid;
alignas(T) char storage[sizeof(T)];
T& get_ref() const { return static_cast((void)storage); }
};
// implementation enabled
Solution
Let's go over it bit-by-bit:
If at least one instantiation of a template is potentially
As a side-note,
- You should sprinkle around quite a lot of
constexprandnoexcept-markers.
If at least one instantiation of a template is potentially
constexpr, marking the template thus is not an error. Not marking it means none is.union{T value;};better shows the intent and is simpler thanalignas(T) char storage[sizeof(T)];.
- Do not values with byte-for-byte unless they are trivial types. Doing so is simply wrong.
- The move-ctor should move, not swap. That might be far less expensive anyway.
- Your base variant should gain a specialization for trivially copyable types.
- Your base-variant should have a variadic-template ctor for constructing the value directly.
- Your references-variant does not need a separate valid-flag:
*NULLis not valid, thus a null pointer serves adequately.
- Only include the minimal headers you need in your include-file.
- Do not use an unnamed namespace, or every translation-unit will have its own unique template (which is different from all the others).
As a side-note,
boost::optional should serve for the case you describe.Context
StackExchange Code Review Q#68686, answer score: 6
Revisions (0)
No revisions yet.