1

My class' interface includes an accessor to an object which may not exist. Currently, it returns a pointer which may be null. I would like to replace the pointer with std::optional as suggested here. The accessor has a const overload which uses Meyers' const_cast trick to avoid repeating the same code twice.

In short, I want to replace this:

T const * MyClass::get() const { 
    /* non-trivial */ 
}
T * MyClass::get() { 
    return const_cast<T *>(const_cast<MyClass const *>(this)->get()); 
}

with this:

std::optional<T const &> MyClass::get() const { 
    /* non-trivial */ 
}
std::optional<T &> MyClass::get() {
    auto t = const_cast<MyClass const *>(this)->get();
    return t ? std::optional<T &>(const_cast<T &>(* t)) : std::nullopt;
}

The replacement seems unsatisfactory because:

  1. it introduces a branch;
  2. the additional complexity somewhat defeats the goal of making the overload be lightweight (and trivially optimized away by the compiler).

I am assuming that the std::optional specialization for a reference can basically boil down to little more than a pointer with added safety and wonder therefore if there's some way to preserve the simplicity of the pointer solution. Is there a more satisfactory way to write the accessor overload to use std::optional?

Community
  • 1
  • 1
John McFarlane
  • 5,528
  • 4
  • 34
  • 38
  • 2
    Are you sure you can directly use references with `std::optional`? Reading n3690, it sounds to me like it required an *object type*, such as a pointer or `std::reference_wrapper`. – dyp Sep 19 '13 at 01:14
  • Is there even a compiler that supports `std::optional` already? – us2012 Sep 19 '13 at 11:14
  • 2
    You are not allowed to instantiate std::optional on a reference (the people who made the proposal thought it would be too controversial and might have kept optional from being added to C++1y). From n3690 (C++1y CD) 20.6.2p1: "A program that necessitates the instantiation of template optional for a reference type, or for (possibly cv-qualified) types in_place_t or nullopt_t, is ill-formed." – Nevin Sep 19 '13 at 15:33
  • Thanks @Nevin and @DyP. Should I instead stick with pointers? Does this affect the advice given by @utnapistim [here](http://stackoverflow.com/questions/17422477/when-should-i-use-references-in-c/17423662#17423662) to replace pointers with `std::optional`? Is there some other alternative to pointers which gives me nullness or disengagement while avoiding some of the risks involved with pointers? – John McFarlane Sep 19 '13 at 17:52
  • @us2012, I cannot say whether any C++11 compiler supports the version which was accepted into C++14. I have been experimenting with an older [reference implementation](https://github.com/akrzemi1/Optional) compiled against Clang which supports references. – John McFarlane Sep 19 '13 at 17:59
  • 1
    `optional` is in tip-of-trunk libc++, and accessible using tip-of-trunk clang with -std=c++1y. It static_asserts for reference types. – Howard Hinnant Sep 20 '13 at 21:50
  • 1
    `optional` was just voted out of C++14 into a TS. It will likely move to `` in libc++ shortly. – Howard Hinnant Sep 29 '13 at 17:17
  • @HowardHinnant thanks for the update. I'm revising my answer accordingly. – John McFarlane Oct 02 '13 at 21:14

1 Answers1

2

As mentioned by others, instantiating std::optional with reference type is ill-formed in the c++14 standard. (See N3690 20.6.2.) Thus using std::optional as a drop-in replacement for a pointer (that points to a single object whose absence is represented by a value of nullptr) is not viable unless you are willing to copy the object by value, rather than by reference.

However, the specification leaves the door open to adding such functionality in the future. Additionally, section 7.15 of N3672 suggests a workaround using std::reference_wrapper.

Update: Additionally, @HowardHinnant informs me that inclusion in the standard has been punted out of c++14 altogether.

John McFarlane
  • 5,528
  • 4
  • 34
  • 38