Переходя к ссылочного аргумента по значению

Рассмотрим эту простую программу:

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';

Я знаю, что С++ 11 предоставил нам всевозможные инструменты типа, возможно ли, что я могу просто заставить это значение к не ссылочному типу и передать это inline, не создавая временные?

Ответы

Ответ 1

Решение может быть следующим (даже если вы делаете временный)

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

Вы можете написать простую функцию, которая принимает ссылку и возвращает значение. это "преобразует" ссылку в значение. Это создает временное, но оно неназванное и будет уничтожено в конце полного выражения. Что-то вроде

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

Живой пример

Ответ 3

Вы можете преобразовать заданное значение в 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) только унарный оператор +, который является 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';

Очевидно, что любой из них по-прежнему создает временное. Я не думаю, что вы можете уйти от этого.

Ответ 4

В этом конкретном случае (когда старое значение является первым вектором), вы можете отменить порядок подстановки с помощью 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;
 }

p.s.: Извините за мой плохой английский.

Ответ 5

Чтобы быть более явным, вы можете использовать int() в качестве конструктора для создания временного:

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

Вместо добавления значения. См. Демо.

Ответ 6

Один вкладыш, который должен работать для любого типа, а не только числовой:

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

или без создания дополнительного поля:

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})));