Почему мой std :: ref не работает должным образом? - PullRequest
1 голос
/ 23 февраля 2020

std::ref дает вам lvalue-reference к чему-то. Эта ссылка заключена в объект, который вы затем можете передать либо по ссылке, либо по значению.

Ожидаемое поведение приведенного ниже кода заключается в том, что он печатает i is 2, но печатает i is 1. Почему это так?

Почему у меня такое ожидание? Поскольку я передаю tmp через std::ref в wrapper. В обертке ссылка захватывается значением. Я бы предположил, что, поскольку я использую std::ref, это значение по-прежнему является ссылкой на tmp. Я изменяю tmp и ожидаю, что f отражает это изменение.

Поиграйте с кодом здесь.

#include <iostream>
#include <functional>

template<typename F>
auto wrapper(int i, F func) {
    return [=]() { return func(i); };
}

void f(int i) {
    std::cout << "i is " << i << '\n';
}

int main() {
    int tmp = 1;
    auto func = wrapper(std::ref(tmp), f);
    tmp = 2;
    func();
}

Ответы [ 2 ]

2 голосов
/ 23 февраля 2020

Причина, по которой это не работает, заключается в том, что ваша wrapper функция принимает int в качестве аргумента.

std::ref возвращает std::reference_wrapper. Когда вы передадите его функции, которая хочет int, вы получите неявное преобразование, и вы больше не будете работать со ссылкой.

Если вы измените сигнатуру функции для использования std::reference_wrapper, это даст ожидаемый результат.

#include <iostream>
#include <functional>

template<typename F>
auto wrapper(std::reference_wrapper<int> i, F func) {
    return [=]() { return func(i); };
}

void f(int i) {
    std::cout << "i is " << i << '\n';
}

int main() {
    int tmp = 1;
    auto func = wrapper(std::ref(tmp), f);
    tmp = 2;
    func();
}
1 голос
/ 23 февраля 2020

Вам необходимо изменить сигнатуры функций, чтобы принимать ссылки:

  1. auto wrapper(int& i, F func) {...}
  2. void f(int& i) {...}

, а также производить лямбда-захват по ссылке return [&]() { return func(i); };. Тогда вам не нужно std::ref.

Полный код будет выглядеть следующим образом:

#include <iostream>
#include <functional>

template<typename F>
auto wrapper(int& i, F func) {
    return [&]() { return func(i); };
}

void f(int& i) {
    std::cout << "i is " << i << '\n';
}

int main() {
    int tmp = 1;
    auto func = wrapper(tmp, f);
    tmp = 2;
    func();
}

Теперь приведенный выше код будет напечатан:

i is 2

Если вы все равно хотел бы использовать std::ref, тогда ваша функция шаблона должна иметь следующую подпись:

template<typename F>
auto wrapper(std::reference_wrapper<int> i, F func) {...}
...