Skip to content

Proposal for value_ref<T> alias template #363

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

Closed
andyprowl opened this issue Oct 27, 2015 · 10 comments
Closed

Proposal for value_ref<T> alias template #363

andyprowl opened this issue Oct 27, 2015 · 10 comments
Assignees
Labels

Comments

@andyprowl
Copy link

Concerning "F.20: Use a const T& parameter for a large object", I'd like to propose the following alias template:

template<typename T>
using value_ref = T const&;

This would document the fact that the function is concerned with the value of an object, rather than its address in memory - although its parameter binds directly to the argument rather than creating an independent copy of it.

This clarifies the semantics of the function without requiring inspection of its definition - for instance, it will be clear that the function won't store the address of the argument anywhere, that there won't be lifetime issues, and that the argument is taken by reference only to avoid pessimization.

In other words, I think it is important for a client to know whether a function needs a value or an object, and T const& is ambiguous about that. We could use the type system to provide semantic annotation, just as owner<T*> does.

Tools may - for instance - detect attempts to take the address of a value parameter and report it (if the function only cares about the value, taking the address could be a suspicious operation leading to lifetime issues). But even without support from tools, this will make it easier for the user to reason about a function.

I personally find this article very convincing, and although I am aware that it is somewhat in contrast with the current guidelines, I thought you might find it inspiring too.

@no-longer-on-githu-b
Copy link

It seems the opposite (e.g. addr_ref) would be less obtrusive, as merely using the value is much more common than using the address. Also, lack of lifetime checking in C++ makes storing the address inherently dangerous, so you want that to stand out.

@andyprowl
Copy link
Author

I see your point. I think object_ref would be a better name than addr_ref (considering the value/object distinction mentioned in the OP), but this is bikeshedding. Technically, value_ref<T> and object_ref<T> are not mutually exclusive either.

@germandiagogomez
Copy link

  • out<T>
  • non_null<T*>
  • value_ref<T>
  • inout<T>
    ...

Are we sure we should start to name absolutely every specific use case we can find in our code? We could end up with a lot of cognitive overhead. I find reasonable for some cases, but maybe not for all. Just a thought.

@andreabrambilla
Copy link

out<T> and non_null<T*> impose verifiable constraints on the corresponding instance (respectively don't read from this, this cannot be null).
I don't think value_ref<T> would provide any added value in this sense

@andyprowl
Copy link
Author

As I wrote in the OP, the point is to specify what the function cares about - does it need a value or an object? It's an important semantic distinction. The user would know from the function signature whether they need to care about lifetime issues or not. T const& doesn't tell you that. Tools may detect attempts to "care about" the address of a value_ref object (e.g. storing a pointer to it or forwarding the argument to a function that cares about an object rather than a value).

@andreabrambilla
Copy link

Sorry, I misread the original post, it is a good point.
I agree with what rightfold wrote, it would be good to highlight when a function actually requires an object ref, since it is an uncommon and potentially dangerous situation. But this may be inconsistent with other template alises:
out<T> -> you CANNOT read from this
owner<T> -> you CANNOT delete this
not_null<T> -> this CANNOT be null
while:
addr_ref / object_ref-> the function CAN use the address
value_ref<T> -> you CANNOT use the address of this
The second options seems to be more consistent with the other guidelines.
Also, I guess that the addr_ref<T> approach would be more difficult to implement, since a static analyser should check all the naked T const& parameters, while no special check is required on the addr_ref<T> parameters.

@BjarneStroustrup
Copy link
Contributor

On 10/30/2015 8:09 AM, andreabrambilla wrote:

Sorry, I misread the original post, it is a good point.
I agree with what rightfold wrote, it would be good to highlight when
a function actually requires an object ref, since it is an uncommon
and potentially dangerous situation. But this may be inconsistent with
other template alises:
|out| -> you CANNOT read from this
|owner| -> you CANNOT delete this

Wrong: "owner" means "delete or pass to another owner"

|not_null| -> this CANNOT be null
while:
|addr_ref / object_ref|-> the function CAN use the address
|value_ref| -> you CANNOT use the address of this
The second options seems to be more consistent with the other guidelines.
Also, I guess that the |addr_ref| approach would be more difficult
to implement, since a static analyser should check all the naked |T
const&| parameters, while no special check is required on the
|addr_ref| parameters.

Note that we need to minimize annotations. If programmers must write
lots of annotations code gets brittle, errors will be made, and the
approach will not scale.


Reply to this email directly or view it on GitHub
#363 (comment).

@NicolBolas
Copy link

I think value_ref is really the wrong way to go about expressing the concept.

What you're trying to do is disambiguate between uses of a const T& parameter which mean:

1: A function that takes a non-modifiable reference to what is potentially a temporary object.

2: A function which is going to store a reference to an object, but in this case it just so happens to be a const reference.

To me, the answer is simple. If you want 1, you use const T&. If you want 2, you should be using not_null<const T*>. By doing so, you force the caller to get a pointer to the object. And since you can't get a pointer to a temporary, you tell the caller in no uncertain terms that he must give you a non-temporary object. Thus, you stop any such problems before the static analyzer even gets involved.

Also, most people who store such things will be storing them as pointers. So you may as well get the pointer ASAP and make it clear.

Generally speaking, I would say that getting the address of a reference is a code smell. Obviously there are plenty of valid uses for it. But it is a bit dubious.

@germandiagogomez
Copy link

Generally speaking, I would say that getting the address of a reference is a code smell.

👍

@BjarneStroustrup
Copy link
Contributor

I think we should assume that more users will know the very common idiom const T& than will know an alias. Too many aliases can make code impenetrable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants
@NicolBolas @andreabrambilla @andyprowl @no-longer-on-githu-b @BjarneStroustrup @gdr-at-ms @germandiagogomez and others