Why C++ Does Not Need C#-like Properties

C++ is a very complex language, yet there seems to always be a little more room for even more complexity. One of the often requested features of C++ are C# properties syntax. The C# properties syntax offers two distinct advantages, the most relevant currently being something which C++ does not need (and can even be considered harmful in C# code).

A C# property transcribed to C++ might look something like this:

class MyClass {
  int _my_property;
public:
  int my_property {
    get { return _my_property; }
    set { _my_property = value; }
  }
};

There are several problems here. Is the getter const? If it is, what if we need it to not be const? Does it return by value or reference? Does the setter take its new value by reference? What about moveable types and rvalue references? What if we have a need to overload? Attempting to solve these problems, we make the getter and setter a little bit more like function definitions:

class MyClass {
  int _my_property;
public:
  int my_property {
    auto get const { return _my_property; }
    set (const auto& cref) { _my_property = cref; }
    set (auto&& rref) { _my_prroperty = rref; }
  }
};

In these examples we assume that auto would be deduced to be the type of the property. Note the best choice, but it works.

This still isn't handling templates at all. What if we want to have setters or getters that are templates? What if we want the whole property templated? These are complex problems.

Let's take a step back and examine another problem. Properties are often criticized for their ability to hide complex code. Consider this snippet:

struct FooledYou {
  int first;
  int second {
    int get const { return some_really_expensive_calculation(); }
  }
};

FooledYou f = get_f();
std::cout << f.first + f.second << std::endl;

It's not at all obvious from that last line that some_really_expensive_calculation is going to be invoked. Granted, even without properties, operator+ or operator<< could be doing all kinds of unexpected things, which is a frequent complaint about C++ already, but certainly we don't want to make things worse.

An alternative syntax might make all property accesses look like functions:

class MyClass {
  int _field;
public:
  int field {
    int get() const { return _field; }
    void set(int v) { _field = v; }
  }
};

MyClass c;
c.field(1);
std::cout << c.field() << std::endl;

Now it's slightly more obvious that the field accessor is more than simple member accessors in plain structs and classes.

The best thing about this idea is that we need zero additional syntax. The above example is trivially written in C++03 (don't even need C++11) as:

class MyClass {
  int _field;
public:
  int field() const { return _field; }
  void field(int v) { _field = v; }
};

The getter can be declared const or non-const as desired. It can be templated. In C++11, it can be overloaded for reference object types. The setter likewise can be templated, overloaded for different types or varieties of refernces, and so on. It just works as is. And with an arguably superior syntax.

Note that this is already the standard approach in the STL. The size property of a container is accessed via size(). The empty property is empty(). These are typically read-only properties, with alternative methods to change the values like resize(), but this again can be considered a superior approach in the general case. Having a size(int) member function for the sake of reflected serialization is not a difficult stretch for new code, and existing reflection mechanisms are generally quite amenable to getters and setters having completely different names.

The second feature of C# properties that is important, and which C++ does not offer, is the ability to query a property as a distinct entity in the C# reflection system. This allows, for instance, a serialization system to innately know how to both read and write a particular property as the getter and setter are logically grouped together. This problem is tractable in C++ with some work today with external metadata definitions, and a future compile-time introspection system would make it even easier.

There is motion in the committee towards some form of C++ introspection built-in to the language, though the exact form and feature set is yet to be determined. The only way that C# properties would remain an attractive feature for C++ would be if this introspection system lacked the necessary capabilities to group member functions by name, which is all a property is in C++.

C++ needs reflection. It does not need C# properties.

Sean Middleditch

Read more posts by this author.