C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Named Return Value Optimisation [[nrvo]]

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Tue, 10 Feb 2026 16:49:55 +0100
wt., 10 lut 2026 o 16:28 Alejandro Colomar
<une+cxx_std-proposals_at_[hidden]> napisaƂ(a):
>
> Hi Marcin,
>
> On 2026-02-10T16:05:00+0100, Marcin Jaczewski wrote:
> [...]
> > > > I mean something diffrent, I word this wrongly.
> > > > I means that variable itself should have attribute like:
> > > >
> > > > ```
> > > > typeof(int (void)) *fp2 [[nodiscard]];
> > > > ```
> > > >
> > > > And this would fit the definition of `f` as its attribute is linked to
> > > > name not to its type.
> > > > And this will be fine in the current paradigm.
> > >
> > > That would be the wrong choice. The attribute should be part of the
> > > type, to make sure type conversions are okay.
> > >
> > > Let's make it more complex:
> > >
> > > [[nodiscard]] int f(void);
> > > int g(void);
> > >
> > > void h(int (*)(void) );
> > > void i(int (*)(void)[[nodiscard]]);
> > >
> > > int
> > > main(void)
> > > {
> > > h(&f); // This should be diagnosed.
> > > i(&f);
> > > h(&g);
> > > i(&g);
> > > }
> > >
> > > Essentially, [[nodiscard]] should behave like a function qualifier, so
> > > that pointers to functions should be able to gain it but not discard it.
> > >
> > > Otherwise, we have a hole.
> >
> > But this exactly same case I showed, if you change attribute to argument:
> > ```
> > void i(int (*arg [[nodiscard]] )(void));
> > ```
> > This is the property of the argument of the `i` function.
>
> You had to name the argument, when this is really a property of the
> type, and should be expressible in an abstract declarator.
>
> >
> >
> > Only problem start when you try pass `i` itself to another function:
> >
> > ```
> > void foo(void (*)(int (*)(void)));
> > ```
> >
> > Here its become probably very painful to handle it but how many times
> > you need arbitrary nesting?
>
> And indeed this shows that the right thing to attribute is the type.
> It's not a matter of pain; it's a matter of correctness.
>

How `[[nodiscard]]` is part of type? it's more usage of value.


> > Besdied, we can avoid all this by baning both cases like:
> >
> > ```
> > h(&f); // error
> > i(&f); //error too
> > h([[trust_me_bro]]&f); // ok, explicit "cast"
> > i([[trust_me_bro]]&f); // ok, explicit "cast"
> > ```
>
> This is bad; really bad. I don't want the compiler to trust myself.
>

Do not in 99.99% cases you only call functions like this directly?
How many times do you send `malloc` as a callback to other functions?
And if you do this many times, why not make custom type that itself have this
attribute? if you need to guarantee that `int` can't be discarded in some nested
callback hell then maybe the problem lies elsewhere? I see more
as local helper for corner cases like `empty()` not to build new types.

Beside if you allow attributes aspart of type system then you do not answer me:

```
std::is_same_t<int, [[foo]] int>
```

is true? Can I overload over attributes? Can I have template
specialization based on it?
How name mangling will handle it?

> It's like if we said we had to use
>
> foo(const bar);
>
> to avoid diagnostics about const-corectness misuses. This is the wrong
> approach. Const correctness is verified by the compiler, and attributes
> should be verified by the compiler too.
>
> > > > > > and this could be still ignorable.
> > > > >
> > > > > In part, this is a problem because either
> > > > >
> > > > > a) ignorability of attributes is the problem
> > > > > or
> > > > > b) [[nodiscard]] should not be ignorable, and should be replaced by a
> > > > > non-ignorable attribute.
> > > > >
> > > > > And if b) is chosen, then few very ignorable attributes would remain;
> > > > > so that ignorability of attributes would render attributes DoA. *The*
> > > > > truly ignorable attribute is [[maybe_unused]].
> > > > >
> > > > I can imagine many others that could work fine and be enforced by the compiler
> > > > but safely ignored if the compiler does not bother implementing them.
> > > >
> > > > ```
> > > > int* foo(int* b [[restrict(A,B)]], int* c [[restrict(C)]])
> > > > [[restrict(A,B,C)]]
> > > > {
> > > > *b += 2;
> > > > *c += 2;
> > > > return *b < *c ? b : c;
> > > > }
> > > > ```
> > > >
> > > > Now if the compiler regonzie this attribute then he could only do 2
> > > > loads as pointers should not overlap.
> > > > If not, then it only generates less optimal code. if the programmer
> > > > lied then we get UB, same as `[[noreturn]]`.
> > >
> > > restrict is terrible, and your suggested [[restrict]] is similarly
> > > terrible.
> > >
> > > A [[restrict]] replacement of restrict should prioritize diagnostics
> > > over optimizations. Thus, it would end up being similar to
> > > [[nodiscard]].
> > >
> > > Tricky question: should the following code result in a diagnostic?
> > >
> > > alx_at_devuan:~/tmp$ cat restrict.c
> > > void f(int *p [[restrict]], int **pp [[restrict]]);
> > >
> > > int
> > > main(void)
> > > {
> > > int i = 42;
> > > int *p = &i;
> > >
> > > f(p, &p);
> > > }
> >
> > At least my "restric" have explicit arbitrary groups, and would reject
> > this code as values are from the same "group".
>
> Yeah, I wasn't going into that. I kept it simple, for the question.
>
> > Beside I would here ask programet to be explicit:
> >
> > ```
> > f(p, [[trust_me_restric]]&p); //otherwise diagnostic
> > ```
>
> This is wrong. Programmers should not be trusted for correctness.
>
> > overall I would use my restic too to handle life time ranges in program like:
> > ```
> > [[restrict(stack)]]
> > [[restrict(temporal)]]
> > [[restrict(global)]]
> > ```
> >
> > Of course it will be painful when you use multiple nested `*` but we
> > probably should not mix diffrent group too much.
> >
> > >
> > > Similarly tricky question: should the following code result in a
> > > diagnostic?
> > >
> > > alx_at_devuan:~/tmp$ cat restrict.c
> > > void f(int *restrict p, int **restrict pp);
> > >
> > > int
> > > main(void)
> > > {
> > > int i = 42;
> > > int *p = &i;
> > >
> > > f(p, &p);
> > > }
>
> You didn't reply to the tricky questions. Please reply, as they are
> quite important regarding the behavior and design of restrict and any
> replacements for it.
>

This is the same code as the previous example. This means my answer
is related to this too. If I recall C would say is ok, I would say is not.
But this depends on how you choose to model it.

I prefer to look at this as related memory groups, not that these
pointers overlap.


btw we bit do offtomic as this thread is about nrvo, I could continue this but
others would not like too much spam on only tangentially related topics.

>
> Cheers,
> Alex
>
> --
> <https://www.alejandro-colomar.es>

Received on 2026-02-10 15:50:12