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?
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);
}
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
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.
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.
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.
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);
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})));
来源:https://stackoverflow.com/questions/37967446/passing-to-a-reference-argument-by-value