7

So I asked this question and I was tinkering around with solving it via static_cast. (Incidentally it does solve the problem, I'm just not sure if I understand why.)

In the code:

vector<int> foo = {0, 42, 0, 42, 0, 42};
replace(begin(foo), end(foo), static_cast<int>(foo.front()), 13);

Is the static_cast simply constructing an R-Value int? What's the difference between that and just the call:

replace(begin(foo), end(foo), int{foo.front()}, 13);

EDIT:

As inferred by the answers static_cast does seem to construct an R-Value int: http://ideone.com/dVPIhD

But this code does not work on Visual Studio 2015. Is this a compiler bug? Test here: http://webcompiler.cloudapp.net/

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    I don't have any standard's quote to back me up, but I think you can use `+foo.front()` to get a copy of the value, instead of a reference to the symbol – KABoissonneault Jun 22 '16 at 13:36

2 Answers2

7
  1. Yes, it is the same as int{...}, unless .front() returned a type that required a narrowing conversion. In that case, int(...) would be identical.

  2. In the case of programmer error, static cast is marginally less likely to do something dangerous, like convert a pointer into an int than int(...).

Note eliminating the cast results in undefined behaviour as the front element is modified by the replace operation, and that could break std::replace.

I would use

template<class T>
std::decay_t<T> copy_of(T&& t){return std::forward<T>(t); }

myself here.

As for why this isn't working in MSVC...

MSVC helpfully takes situations where you cast a variable of type T to a T and proceeds to do nothing. This breaks your code.

There is a compiler flag (/Zc:rvalueCast) you can use to make MSVC no longer break your code.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Is `copy_of`standard, or you're saying that I should just implement that? – Jonathan Mee Jun 22 '16 at 13:26
  • @jona I was suggesting to implement that, as it has a number of use cases, and eliminates needless type repetition. – Yakk - Adam Nevraumont Jun 22 '16 at 13:30
  • So I've clarified my question by pointing out the difference between Visual Studio and gcc. Sorry to move the goalposts on you. – Jonathan Mee Jun 22 '16 at 13:52
  • 1
    _"MSVC helpfully takes situations where you cast a variable of type T to a T and proceeds to do nothing. This breaks your code."_ lol – Lightness Races in Orbit Jun 22 '16 at 14:12
  • 2
    _"By default, /Zc:rvalueCast is off. For conformance and to eliminate errors in the use of casts, we recommend that you use /Zc:rvalueCast."_ What the hell – Lightness Races in Orbit Jun 22 '16 at 14:13
  • @LightnessRacesinOrbit They didn't want to break legacy behavior. As an example, `(double)x = 3.14;` will "work" if `x` is a double under MSVC 2012. – Yakk - Adam Nevraumont Jun 22 '16 at 14:21
  • 3
    @Yakk: Sure, I get that, but if they recommend the flag be turned on, it should be on by default. This way they break "legacy" behaviour in terms of the behaviour of every other compiler! Users who _need_ the legacy behaviour should be required to look it up and turn it on. IMO. It just seems like a really strange contradiction in that documentation. – Lightness Races in Orbit Jun 22 '16 at 14:44
  • 1
    @LightnessRacesinOrbit No. The default behavior of almost every compiler tends to be "do not break code that worked in the previous version of *this compiler*, without lots of warning". You can see this in gcc, which didn't break `std::string` compatibility for a *long* time. There are far more customers of MSVC2015 upgrading from MSVC2012 than there are those coming from gcc or clang. – Yakk - Adam Nevraumont Jun 22 '16 at 14:49
  • @Yakk: Then they should not say they recommend the flag be turned on. – Lightness Races in Orbit Jun 22 '16 at 14:52
  • @light you should turn it on *then fix the build breaks*. It is worth the effort: I did it on a many mega line code base. It breaking the moment you upgrade would have been annoying, as we had other stuff to test and fix at that time. – Yakk - Adam Nevraumont Jun 22 '16 at 14:59
  • @Yakk: Meh. It should have been clearly documented that this feature came into play then you would have made _fixing the build breaks_ part of your upgrade task. Just making it part of that other stuff :) The way they did it is like upgrading but not upgrading and, again, it's resulted in effectively contradictory advice in the documentation. (To be clear, I wholly support at least having the option to enable/disable the behaviour. Backward compatibility is of course important.) – Lightness Races in Orbit Jun 22 '16 at 15:03
  • Yup, the "/Zc:rvalueCast" solves the problem. To future readers you can set that in your project by going to your "Property Pages" > "Configuration Properties" > "C/C++" > "Command Line" and enter "/Zc:rvalueCast" under "Additional Options" – Jonathan Mee Jun 22 '16 at 18:24
  • It appears that it is possible to default this behavior across all Visual Studio projects without modifying them individually: http://stackoverflow.com/q/37976395/2642059 – Jonathan Mee Jun 23 '16 at 11:29
  • wow What a find! Wonder if there's more stuff like that we don't know about. – KeyC0de Sep 29 '18 at 22:47
5

Member function front returns a reference to the first element of a non-empty vector.

On the other hand standard algorithm replace declared like

template <class ForwardIterator, class T>
  void replace (ForwardIterator first, ForwardIterator last,
                const T& old_value, const T& new_value)

takes the third parameter also by reference. Thus in general the first element of the vector can be changed by the algorithm and as result processing of other elements of the vector by the algorithm can be incorrect.

Using static_cast a temporary object is created and will not be changed by the algorithm So the processing of all elements of the vector will be correct.

As for me then I suggested a C++ proposal to use keyword auto in such cases. For example

replace(begin(foo), end(foo), auto( foo.front() ), 13);
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    The `auto` suggestion is very elegant. +1 – Jonathan Mee Jun 22 '16 at 13:25
  • 1
    The auto suggestion fails to compile: http://coliru.stacked-crooked.com/a/27328ba4ae68d68d – NathanOliver Jun 22 '16 at 13:26
  • 1
    @NathanOliver As said in the answer, it's just a proposal – KABoissonneault Jun 22 '16 at 13:27
  • 1
    @NathanOliver It is just my proposal. I need to write it at first. I only suggested it at the ISO C++ forum.:) – Vlad from Moscow Jun 22 '16 at 13:27
  • Ah. I didn't see that. – NathanOliver Jun 22 '16 at 13:27
  • 1
    And I doubt such a proposal would be accepted, partly for the same reason why we can't overload functions based on the return value: the return type of expressions in C/C++ don't depend on where they're going to be caught. – KABoissonneault Jun 22 '16 at 13:29
  • @KABoissonneault I mean it's just a `copy` corollary to C++11's [`move`](http://en.cppreference.com/w/cpp/utility/move) right? Perhaps you could enlighten me on what's not to love there? – Jonathan Mee Jun 22 '16 at 13:31
  • @KABoissonneault It can be discussed at the ISO forum. I can not show the exact reference now but you could look through my themes opened there. – Vlad from Moscow Jun 22 '16 at 13:31
  • @JonathanMee Hm, I think I've misunderstood the nature of the question. I thought you wanted to change the value by `front` to the type `int`, because I thought it was something else, but it's actually `int&` so you just want the value. I think you could use the unary `operator+` for that here. – KABoissonneault Jun 22 '16 at 13:33
  • @KABoissonneault An excellent suggestion. It's actually been mentioned [here](http://stackoverflow.com/a/37967837/2642059). This question was just the result of me trying things, and not being certain whit was happening under the hood with `static_cast`. – Jonathan Mee Jun 22 '16 at 13:37
  • @VladfromMoscow So I've clarified my question by pointing out the difference between Visual Studio and gcc. Sorry to move the goalposts on you. – Jonathan Mee Jun 22 '16 at 13:51