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?

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Can't it move from the `const` local anyways via as-if rule? – Baum mit Augen Jul 04 '16 at 20:42
  • 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. – Marc Glisse Jul 04 '16 at 20:42
  • Shouldn't a compiler be able to determine that the lifetime of `p` will end and move anyway? – Pixelchemist Jul 04 '16 at 20:42
  • 1
    @Pixelchemist that's what it does. It effectively puts a `std::move(..)` around it. But `p` is const, so it won't move from it. – Johannes Schaub - litb Jul 04 '16 at 20:44
  • 1
    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. – Marc Glisse Jul 04 '16 at 20:47
  • Huh, apparently it does matter. http://melpon.org/wandbox/permlink/osnNfTNAr6zLw88w http://melpon.org/wandbox/permlink/pRMMeXBBukM1ohEW Example does not work for clang though, it manages to RVO anyways. – Baum mit Augen Jul 04 '16 at 20:51
  • 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? – vsoftco Jul 04 '16 at 20:52
  • 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? – juanchopanza Jul 04 '16 at 20:52
  • 1
    @juanchopanza it's allowed by the spec, but the impl can't do it – Johannes Schaub - litb Jul 04 '16 at 20:53
  • is `resource_ptr` a typedef for raw pointer or what – M.M Jul 04 '16 at 21:06
  • @M.M a movable type which has a more expensive copy constructor. vector, shared_ptr.. – Johannes Schaub - litb Jul 04 '16 at 21:11

1 Answers1

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    Is there any case where mandatory elision (C++17) changes things here? – Marc Glisse Jul 04 '16 at 20:50
  • 1
    @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{};`. – Howard Hinnant Jul 04 '16 at 20:53
  • @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. – Johannes Schaub - litb Jul 04 '16 at 20:56
  • @HowardHinnant I haven't read the new rule at all yet, thanks for the quick explanation. – Marc Glisse Jul 04 '16 at 20:56
  • 2
    @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. – Howard Hinnant Jul 04 '16 at 20:58
  • @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. – Marc Glisse Jul 04 '16 at 20:58
  • Ah, I still have to learn about the new C++17 rules. Mandatory copy elision sounds interesting. – Johannes Schaub - litb Jul 04 '16 at 20:59
  • @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. – Johannes Schaub - litb Jul 04 '16 at 21:01
  • @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). – Howard Hinnant Jul 04 '16 at 21:09