New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
not_null<T> construction from T: is the "explicit" constructor explicit enough? #257
Comments
Thank you, this is very valuable input. My main objection would be that, conventionally, I'd prefer to give the not-null "entry point" a different name. Perhaps Of course, subsequent objections would be concerned with backward compatibility and general Core Guidelines compliance. But that's a bigger issue. For now I'd be happy to have this change under a config switch so we can experiment with it. |
I understand and agree; maybe
True; this may also warrant discussion with the Core Guidelines team.
I'll prepare a PR. (And leave |
I'd prefer to use verbs to name functions. Why not
Well, we already have |
To me, the name
Calling it (I agree with the usage of |
I think I understand why you find |
|
I now implemented However, I've had second thoughts about this. The problem you've run into is a much more general pitfall, and it affects explicit constructors everywhere, not just the I'm not saying In fact this problem (inadvertently called explicit constructors) seems so commonplace that I'd expect Clang or GCC to have a warning for this. I didn't check but it might be worth searching for one. |
Problem statement
Last year, I described Dropbox nn's constructor this way (see my comment in issue #200):
Since then, I have revised my opinion: I think it would be better if you couldn't create a
not_null
from a nullable without explicitly callingmake_not_null
. The following is the description of the evolution of my company's codebase, highlighting the problem with our current (explicit) constructor, then my proposed solution.Case study
Original code
We had several old, pre-C++11 factory methods:
The client code used
std::auto_ptr
to manage the memory:Introducing unique_ptr
After migrating to C++11, we changed our
auto_ptr
s tounique_ptr
s, an made our factory methods returnunique_ptr
:Note that we did the
auto_ptr
->unique_ptr
rather mindlessly, not changing the syntax in the client apart from changing the smart pointer's name.Introducing not_null
When
not_null
became able to handleunique_ptr
, we changed the return type of the factory functions tonn_unique_ptr<T>
(i.e.gsl::not_null<std::unique_ptr<T>>
):During this transition, we made a mistake, and changed the type of
thing3
even though it should have been left as a plain oldunique_ptr
. The above code compiled without errors, and the mistake was only discovered during testing, whencreateThing3()
was configured to returnnullptr
.Proposed solution
The events described above led me to believe that
not_null<T>
should not have a converting constructor from T or any other nullable types; the conversion should always be done via explicitly callingmake_not_null()
. In that case,nn_unique_ptr<Thing3> thing3(createThing3());
would result in a compile-time error instead of a run-time error.This could be achieved by stealing the idea from Dropbox nn, and giving it our own spin:
T
->not_null<T>
constructor private, and give it a tag argumentmake_not_null()
a friend ofnot_null
This way, only
make_not_null()
could be used to createnot_null
s, while not impacting the converting constructors between differentnot_null
instantiations. Coupled with my proposal in issue #253, it would mean the only transition between nullable- and not_null-land would be through the two named functionsmake_not_null
andas_nullable
, making everything explicit and (mostly) mistake-free.The text was updated successfully, but these errors were encountered: