Avoid const locals that are returned?
Asked Answered
H

1

21

I always thought that it's good to have const locals be const

void f() {
    const resource_ptr p = get();
    // ...
}

However last week I watched students that worked on a C++ exercise and that wondered about a const pointer being returned

resource_ptr f() {
    const resource_ptr p = get();
    // ...
    return p;
}

Here, if the compiler can't apply NRVO (imagine some scenario under which that is true, perhaps returning one of two pointers, depending on a condition), suddenly the const becomes a pessimization because the compiler can't move from p, because it's const.

Is it a good idea to try and avoid const on returned locals, or is there a better way to deal with this?

Hendecasyllable answered 4/7, 2016 at 20:32 Comment(11)
Can't it move from the const local anyways via as-if rule?Cleanse
In practice, compilers tend to be quite bad at applying (N)RVO in any but the most trivial circumstances, so this is a reasonable concern. A special case is when your "local variable" is actually an argument, where copy elision is sadly forbidden.Tabasco
Shouldn't a compiler be able to determine that the lifetime of p will end and move anyway?Eclampsia
@Eclampsia that's what it does. It effectively puts a std::move(..) around it. But p is const, so it won't move from it.Hendecasyllable
If your type has a copy constructor Type(Type const&&), it will use that. Most people consider that modifying the argument in such a constructor is wrong (it is const), but not all, some find it more important that it is an rvalue. In any case, I personally avoid the const on such variables.Tabasco
Huh, apparently it does matter. melpon.org/wandbox/permlink/osnNfTNAr6zLw88w melpon.org/wandbox/permlink/pRMMeXBBukM1ohEW Example does not work for clang though, it manages to RVO anyways.Cleanse
What's actually the reason for adding const to locals, especially if you expect moves from them? In the worst case, you can const_cast, assuming you started with a non-const object visible outside the body of your function. Can you perhaps clarify?Nonconductor
By "compiler can't apply NRVO", do you mean that it isn't applied by an implementation even though it is allowed, or do you mean it isn't a candidate for NRVO as per the standard?Venetian
@Venetian it's allowed by the spec, but the impl can't do itHendecasyllable
is resource_ptr a typedef for raw pointer or whatGuthrie
@Guthrie a movable type which has a more expensive copy constructor. vector, shared_ptr..Hendecasyllable
R
15

Is it a good idea to try and avoid const on returned locals, or is there a better way to deal with this?

Yes. In fact if resource_ptr is a move-only type, you will get a compile-time error if you try to return one which is const.

This is an example of where "tried-and-true" C++98/03 advice no longer applies in C++11 and forward.

Repression answered 4/7, 2016 at 20:48 Comment(9)
Is there any case where mandatory elision (C++17) changes things here?Tabasco
@MarcGlisse: I'm still learning the new C++17 rules myself. But my current understanding is that mandatory elision will only apply to the case where an unnamed temporary is returned. E.g.: return X{};.Repression
@HowardHinnant would a rule that makes p const till the return statement not better, if there are no other references to p after the return (dtors of locals that are declared after p)? The return would ignore the const. I don't like these "pitfalls" that wait for you every other line of code.Hendecasyllable
@HowardHinnant I haven't read the new rule at all yet, thanks for the quick explanation.Tabasco
@JohannesSchaub-litb: That is certainly a possibility for the future. To date no one has proposed it (that I'm aware of). const on the local disables RVO (copy elision) and the C++11 "auto-move" rules piggy-backed on the RVO rules for the most part.Repression
@JohannesSchaub-litb I am quite sure I have seen several discussions about that point, I don't think they led anywhere, but I don't remember exactly the arguments on both sides.Tabasco
Ah, I still have to learn about the new C++17 rules. Mandatory copy elision sounds interesting.Hendecasyllable
@HowardHinnant I think it does not disable RVO, since it says "with the same type (ignoring cv-qualification) as the function return type". When it applies RVO, the data in the const object stays constant: It's only constructed once (in the return value). That's what I was unsure about aswell, but looked it up and it would RVO it.Hendecasyllable
@JohannesSchaub-litb: You are correct. I misremembered. I would edit that comment, but the edit time has already elapsed. I was getting mixed up with declaring the return type const, which poisons move constructing from such a return type (compile-time error for move-only types).Repression

© 2022 - 2024 — McMap. All rights reserved.