Reinventing std::optional I decided to make my own version of C++17's std::optional

It's basically a class that can optionally hold a variable (as the name suggests), it avoids having to resort to std::pair&lt;bool, T&gt; to check whether a variable is set.

I'd like some feedback regarding the overall implementation

template&lt;typename T&gt;
class Optional {
 public:
  /**
   * The constructor
   */
  Optional() :
    is_set_(false) {}
  /**
   * The constructor
   * @param val The value to set it to
   */
  Optional(const T&amp; val) :
    val_(val), is_set_(true) {}

  /**
   * Sets the value
   * @param val The value to set it to
   */
  void set(const T&amp; val) { val_ = val; is_set_ = true; }

  /**
   * @return The value
   */
  T get() const { return val_; }

  /**
   * Clears the value
   */
  void clear() { val_ = T{}; is_set_ = false; }

  /**
   * @return Whether the value is set
   */
  bool empty() { return !is_set_; }

  /* Operators */

  Optional operator=(const T&amp; val) {
    val_ = val;
    is_set_ = true;
    return *this;
  }

  operator T() const {
    return val_;
  }

  bool operator==(const bool&amp; val) const {
    return (is_set_ == val);
  }

  explicit operator bool() const { return is_set_; }

 private:
  T val_{};
  bool is_set_ = false;
};
Prefer value type semantic

Try to make classes default constructible and equality comparable whenever this makes sense. Currently your Optional class doesn't fulfil neither. The following code will not compile

class A { int a; A() = delete; };

int main()
{
  Optional&lt;A&gt; i;
}


Even though it might make sense to have an optional. Also consider this example

struct B { B() { /* insert something super long */ } }:

int main()
{
  Optional&lt;B&gt; opt_b; // this line takes really long!
}


Here is an example of how to use std::aligned_storage to use uninitialised memory. I think this could help you as well.

Do not return by value if you can

The line

T get() const { return val_; }


Makes a copy of T while returning val_. Imagine val_ is a very big std::string. This could be very expensive in code which calls get() multiple times. If you want read-only access, prefer to use

const T&amp; get() const&amp; noexcept { return val_; }


instead.

Make use of move assignments

Currently you have to copy values into your optional. But what, if you just want to move something big that you want to return from a function? You 
need to add constructors for rvalue-Ts.

Optional(T&amp;&amp; value)
 : val_{std::move(value)}
 , is_set_{true} {}