Search code examples
c++constantsconst-correctnessreference-wrapper

const correctness for custom reference wrapper


I'm using boost::variant to implement type erasure when sending data across network. When the data arrives to the client or server, I'm inspecting its structure to retrieve the underlying information and reporting errors when the structure does not match the one agreed upon in the API. For this purpose, I created a lightweight reference wrapper that holds a reference to the variant and provides a convenient interface for structure inspection and type conversions.

using value_t = boost::make_recursive_variant< /*types, some of them recursive*/ >::type;

class view_t {
public:
  /*non-const methods to access value_*/
private:
  value_t& value_;
};

class cview_t {
public:
  /*const methods to access value_*/
private:
  const value_t& value_;
};

view_t   view(value_t& v){ return v; }
cview_t cview(const value_t& v){ return v; }
cview_t cview(value_t&& v) = delete;
cview_t cview(const value_t&& v) = delete;

Unfortunately, this interface proves to be cumbersome because every aggregate type that has a view member needs to follow the same const/non-const split. I was wondering whether it was legal to merge these two classes into a single view and use a const_cast in the methods that create views like this:

class view_t {
public:
  /*const and non-const methods to access value_*/
private:
  value_t& value_;
};

      view_t view(value_t& v){ return v; }
const view_t cview(const value_t& v){ return const_cast<value_t&>(v); }
const view_t cview(value_t&& v) = delete;
const view_t cview(const value_t&& v) = delete;

Solution

  • Returning const object is mostly useless, as

    auto /*view_t*/ my_view = cview(some_value); // legal
    

    So you lose your constness.

    Modifying const value is undefined behavior,

    your view can be used in a way leading to UB.

    But as long you don't modify const object, you are "fine".

    Splitting into 2 classes is safer, as it cannot be misused.