A bit of background for the operator dot proposal—Bjarne Stroustrup

Save to:
Instapaper Pocket Readability

Over the years, there have been several suggestions and proposals for allowing programmers to define operator.() so as  to be able to provide “smart references” similar to the way we provide “smart pointers.” I first considered that in 1983, but couldn’t solve the problem. In early 2014, Gabriel Dos Reis and I decided that now we really had to solve it: Too much code was getting too complicated and too ugly because of the lack of smart references. Operator -> is good for objects with pointer semantics, but we needed a general way of providing value semantics for a handle.

Operator dot differs from other operators by having its right-hand operand be a name rather than a value: for x.m, m is not the value of some variable it is the name of a member of some class. Ideally, dot would be a binary operator taking into account both x and m for x.m. Many have tried, including Gaby and I, but that quickly gets complicated. The current rules for operator->() and the proposed design for operator.() “solves” those problems by considering the right-hand operand only when looking for possible candidates. This allows composition of interfaces without requiring inheritance. If someone figures out a good and simple way of taking the member name into account in a more general form, we are reasonably sure that we have not proposed anything that prevents that. When designing, you must always try to avoid painting yourself into a corner by closing off possible evolution paths.

Historically, the key sticking point with the operator.() proposals has been how to distinguish operations on the reference object (the handle) from operations on the referred-to object (the value). The current proposal “forwards” every operation to the referred-to object unless that operation has been explicitly declared in the handle. This is simple and quite general. In particular, we can apply operators on a smart reference (e.g., ++r) and have ++ apply to reference is we so desire, but by default it is applied to the referred-to object (as it would for a built-in reference). The papers outline why we think that this is – by far – the design that best balances the needs of the various use cases. The focus is on allowing the user to define a class that is a reference, just like the built-in reference, except that it is “smart” in some way (e.g., it can have a rebind() member) analogously to the way a “smart pointer”  is just like a pointer except that it is “smart” in some way (e.g., takes care of ownership). If you can define or use a smart pointer, you can define or use a smart reference.

The proposal has been quite stable. There have been proposals for alternatives and major extensions, but few suggested modifications. We tried to find a way to prevent a pointer (or reference) to the referred-to object to escape from the smart reference, but couldn’t find a simple way of guaranteeing that so we gave up, quoting “C++ protects against Murphy, not Machiavelli.” We ensured that you can define a matching set of smart . (dot), -> (arrow), * (dereference), and [] (subscript) operators with the relations they have for built-in types.

Add a Comment

Comments are closed.

Comments (3)

0 2

J-S-B said on Feb 24, 2016 06:54 AM:

Hello Bjarne!

Years ago, I contacted you directly via email, and asked you to add just this very feature.
I knew then, that C# was going to hurt the use of the C++ language, without the dot-operator; and it has in my opinion.

Bjarne back then you replied that it was not ~~possible~~ in other words, which saddened me, but that is OK. I'm glad to see you coming forward on this.

Since then, I've gone far deep into C#. I like the C#/.NET world, when it comes to building, but I really miss multiple inheritance, which in the C# world causes me daily grief without it. And I dare say, that C# without multiple inheritance causes everyone loads of wasted development time, especially with C#'s "interfaces", which really waste time, and cause horrifically BUGGY code. But, without C++ having the dot operator, the C#'s lack of multiple inheritance has become the norm.

I say all this, as I know, that if C++ gets the dot operator, it will become the most efficient language, BAR-NONE, because of C++'s multiple inheritance. It will basically give C++ full JavaScript like functionality (as I see it), if the "word" after the dot gets treated just like a string variable to a function call. Or, say, my.Func is equivalent to my::~~something~~(char *pszFunc). That theoretically will allow C++ (in my mind) to do JavaScript like calls of my.~~Some-Name-That-Does-Not-Exist-In-The-Class~~ = ~~function pointer~~, similar to how JavaScript works.

Example:

[removed]

function MyFunc() { alert("Hello!!!"); }
var MyObject = { DataMem_x: "A string!!!", Func_Y: MyFunc };

C++ with dot operator...

class MyClass
{
~~~ something to allow dot operator to add polymorphic functions/data, to the MyClass as if they were part of the class definition ~~~~
}

int FuncY() { return 1; }

void main()
{
MyClass me = new MyClass;
me.DataMem_c = "Hello!!!"; // MyClass does not have "DataMem_c" defined...
me.Func_Y = FuncY;

if (me.Func_Y(8) == 1) { alert(me.DataMem_c); }
}

Just, lol, please be sure to keep C++ 100% backwards compatible, and if you can please, may it work in the .NET world.

Email me some time Bjarne!
John-S-Brumbelow@Hotmail.com
0 0

OmerRosler said on Feb 25, 2016 03:55 AM:

I see in the references that overloading the .* operator was part of an earlier proposal, and I couldn't find any discussion about it.
It seems the same motivation (and design decisions) for the dot operator apply to it as well.
Is there a specific reason it was droped?
0 0

Noah said on Feb 29, 2016 04:30 PM:

You say "Too much code was getting too complicated and too ugly because of the lack of smart references."? I'm sure you're right, but it's not obvious to me how that is. I mean, I'm a big fan of value semantics but is the difference between a "." and a "->" (or an "x" and a "*xp") really that big a deal?

Anyway, that's not my point. I recently implemented a smart/safe compatible replacement for "raw" pointers (that, like raw pointers, can point to the stack) (http://duneroadrunner.github.io/SaferCPlusPlus/#registered-pointers). There is currently no way to do same for "raw" references, of course. This proposed "operator dot" should allow for the implementation of a safe compatible replacement for "raw" references. And personally, I think that it will be one of the most important uses of such an operator, because it would be a key part of (finally) enabling C++ programmers to easily make their code free from the possibility of (inadvertently) accessing invalid memory (just like their Java and C# code).

I haven't really studied the proposal, but as a future "smart reference" implementer, I just want to make sure that I can prevent my "safe" references from being (implicitly) converted to (unsafe) "raw" references. As that would, in part, defeat their purpose.

When implementing the smart/safe pointer it was no problem to prevent implicit conversion to "raw" pointers. It was actually also no problem preventing the "escape" of the real address of the object. That's because I substitute the target object's type with a compatible type publicly derived from the target type that overrides the default "&" operator. In fact, I suggest the conventional way of thinking about smart pointer/reference types as independent of the target object's type is a little bit misguided and limiting. The fact that, say std::shared_ptr, can be implemented without requiring any cooperation from the target's type should be considered idiosyncratic and not a natural property of smart pointers. Indeed, the "cloak and dagger" manipulation that std::make_shared does to the target object's allocation can almost be considered a form of (coerced) cooperation from the target object. And if you really think about it, just the fact that the target object agrees to restrict it's allocation to the heap could be considered very cooperative. So you should really think about it as a smart pointer-target pair where sometimes the pointer has most of the smarts, and other times the target has more of the smarts.

This may sound a bit presumptuous, but I really think you should check out my smart/safe pointer, if only to be aware of the fastest smart pointer available and the only one that can safely target the stack.

And if you check out the rest of the library it belongs to, it might become clear that the next C++ issue that you're going to want to revisit is making the native types (like int) into full-fledged classes (that can act as base classes). Or at least providing some mechanism for user defined classes to mimic their conversion rules so they can act as true "drop-in" replacements for the native types.

The ultimate goal is to provide safe, fast, compatible substitutes for C++'s dangerous elements to make it practical to write C++ code with zero chance of accessing invalid memory or using uninitialized values. We are so close. I can feel it smile