问题
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?
回答1:
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);
}
回答2:
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
回答3:
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.
回答4:
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.
回答5:
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.
回答6:
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);
回答7:
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