Переход к ссылочному аргументу по значению
Рассмотрим эту простую программу:
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';
Когда я написал это, я ожидал получить:
13 42 13 42 13 42
Но вместо этого я получил:
13 42 0 42 0 42
Проблема, конечно, в том, что replace
принимает последние 2 параметра по ссылке. Так что, если кто-то из них окажется в диапазоне, на котором работают, результаты могут быть неожиданными. Я могу решить эту проблему, добавив временную переменную:
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';
Я действительно знаю, что C++11 дал нам все виды инструментов типов. Возможно ли, что я мог бы просто принудительно преобразовать это значение в не-ссылочный тип и передать его в строку, не создавая временный?
7 ответов
Решение может быть следующим (даже если вы делаете временный)
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);
}
Вы можете написать простую функцию, которая получает ссылку и возвращает значение. это "конвертирует" ссылку в значение. Это генерирует временное, но оно безымянное и будет уничтожено в конце полного выражения. Что-то вроде
template<typename T>
T value(const T& ref)
{
return ref;
}
И тогда вы можете использовать его как
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';
}
выход:
13 42 13 42 13 42
Вы можете конвертировать данное значение в rvalue
добиться желаемого эффекта. Приведенный ниже пример работает без определения каких-либо дополнительных функций, просто добавляя ноль к значению.
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';
Или даже (как предположил Jarod42) только унарный +
оператор, который является неактивным:
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';
Очевидно, что любой из них все еще создает временный характер. Я не думаю, что вы можете избежать этого.
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})));
Чтобы быть более явным, вы можете использовать int()
в качестве конструктора для создания временного:
replace(begin(foo), end(foo), int(foo.front()), 13);
Вместо добавления значения. Смотрите Демо.
В этом конкретном случае (когда старое значение является первым из вектора), вы можете изменить порядок подстановки с помощью rbegin()
а также rend()
,
В общем, я не знаю, возможно ли это простым способом без копирования.
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;
}
PS: извините за мой плохой английский.
Один лайнер, который должен работать для любого типа, а не только числового:
replace(begin(foo), end(foo), make_pair(foo.front(),0).first, 13);
или без создания дополнительного поля:
replace(begin(foo), end(foo), get<0>( make_tuple(foo.front()) ), 13);