12

Consider this simple program:

vector<int> foo = {0, 42, 0, 42, 0, 42}; replace(begin(foo), end(foo), foo.front(), 13); for(const auto& i : foo) cout << i << '\t'; 

When I wrote it I expected to get:

13 42 13 42 13 42

But instead I got:

13 42 0 42 0 42

The problem of course is that replace takes in the last 2 parameters by reference. So if either of them happen to be in the range being operated on the results may be unexpected. I can solve this by adding a temporary variable:

vector<int> foo = {0, 42, 0, 42, 0, 42}; const auto temp = foo.front(); replace(begin(foo), end(foo), temp, 13); for(const auto& i : foo) cout << i << '\t'; 

I do know that C++11 gave us all kinds of type tools is it possible that I could simply force this value to a non-reference type and pass that inline, without creating the temporary?

6
  • 8
    Not a solution/answer, but you could write replace(begin(foo), end(foo), int(foo.front()), 13);which could be templatetized using the copy constructor of the object. I feel you have to use a copy of some sort. Commented Jun 22, 2016 at 12:08
  • @thorsan, Actually, this is a (little bit hacky) solution, so you should post it as well. Commented Jun 22, 2016 at 12:12
  • 2
    @thorsan Not the answer I was hoping for, but the more that I look at it the more I like it. I think that you could present a defensible case that is going to be the best and the simplest way to do it. If you were to write that up as an answer you'd at least get an upvote from me. Commented Jun 22, 2016 at 12:13
  • Could you in some way move the input since you are overwriting it anyway? Commented Jun 22, 2016 at 12:13
  • 1
    @thorsan No, I've simplified it in this case to get it into a minimal, complete, verifiable example The case I'm looking at may come from anywhere in the range and I won't know where. Commented Jun 22, 2016 at 12:16

7 Answers 7

9

A solution could be as follows (even though you are making a temporary)

template<class T> void replace_value_of_first(std::vector<T>& v, const T& value) { std::replace(v.begin(), v.end(), T(v.front()), value); } 
Sign up to request clarification or add additional context in comments.

3 Comments

It's being discussed over here but a static_cast provides some additional protection here. Though this is a fine solution without it.
Hmm... I've updated the linked question to express the problem, but this doesn't work on Visual Studio 2015.
For Visual Studio users you can get C++11 compliance so this is supported without modifying individual projects: stackoverflow.com/q/37976395/2642059 But even though this is standard compliant, without the linked step, Visual Studio 2015 will not behave correctly here.
7

You can write a simple function that takes in a reference and returns a value. this will "convert" the reference into a value. This does generate a temporary but it is unnamed and will be destroyed at the end of the full expression. Something like

template<typename T> T value(const T& ref) { return ref; } 

And then you can use it like

int main() { vector<int> foo = {0, 42, 0, 42, 0, 42}; replace(begin(foo), end(foo), value(foo.front()), 13); for(const auto& i : foo) cout << i << '\t'; } 

output:

13 42 13 42 13 42 

Live Example

Comments

4

You can convert the given value into an rvalue to achieve the desired effect. The example below works, without defining any extra functions, simply by adding zero to the value.

vector<int> foo = {0, 42, 0, 42, 0, 42}; replace(begin(foo), end(foo), foo.front()+0, 13); for(const auto& i : foo) cout << i << '\t'; 

Or even (as suggested by Jarod42) just the unary + operator, which is a no-op:

vector<int> foo = {0, 42, 0, 42, 0, 42}; replace(begin(foo), end(foo), +foo.front(), 13); for(const auto& i : foo) cout << i << '\t'; 

Obviously either of these still create a temporary. I don't think you can get away from that.

13 Comments

Yes, that's even better. Updated answer
Or even +foo.front().
Btw, your solution doesn't work in general case for any T (as for std::string).
@JonathanMee: it is a no-op, and don't change the value (even if negative). it is equivalent to his +0.
@JonathanMee this would be actually equivalent as +0 means actually +int()
|
1

In this particular case (when the old value is the first of the vector), you can reverse the order of substitution with rbegin() and rend().

In general, I don't know if it's possible, in a simple way, without make a copy.

int main () { std::vector<int> foo = {0, 42, 0, 42, 0, 42}; std::replace(foo.rbegin(), foo.rend(), foo.front(), 13); for(const auto & i : foo) std::cout << i << '\t'; std::cout << std::endl; return 0; } 

p.s.: sorry for my bad English.

Comments

1

To be more explicit you can use int() as a constructor to create a temporary:

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

Instead of adding a value. See Demo.

1 Comment

You're a little late to the party: stackoverflow.com/a/37967761/2642059 But that and this are the best solutions.
1

One liner that should work for any type, not only numeric:

replace(begin(foo), end(foo), make_pair(foo.front(),0).first, 13); 

or without creating extra field:

replace(begin(foo), end(foo), get<0>( make_tuple(foo.front()) ), 13); 

Comments

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

1 Comment

Haha: stackoverflow.com/questions/37969114/… turns out it doesn't work on Visual Studio 2015.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.