Реализовал ли std :: bind std :: ref и std :: cref для устранения неоднозначности вызова функции? - PullRequest
3 голосов
/ 18 января 2020

Я знаю, что не следует перегружать функцию, поскольку только параметры отличаются только одним из них, передаваемым копией, а другим - ссылкой:

void foo(int x)
{
    cout << "in foo(int x) x: " << x << endl;
}
void foo(int& x)
{
    cout << "in foo(int& x) x: " << x << endl;
}

int main()
{

    int a = 1;
    foo(5); // ok as long as there is one best match foo(int)
    foo(a); // error: two best candidates so the call is ambiguous
    //foo(std::move(a));
    //foo(std::ref(an)); // why also this doesn't work?
}

Так что код, который использует std::bind, может быть например:

std::ostream& printVec(std::ostream& out, const std::vector<int> v)
{
    for (auto i : v)
        out << i << ", ";
    return out;
}

int main()
{
    //auto func = std::bind(std::cout, std::placeholders::_1); // error: stream objects cannot be passed by value
    auto func = std::bind(std::ref(std::cout), std::placeholders::_1); // ok. 

}

Итак, std::ref здесь, чтобы обеспечить передачу по ссылке, а не по значению, чтобы избежать двусмысленности? * Вещество, которое имеет для меня значение: std::bind() реализовал какую-то оболочку для решения этой проблемы?

  • Почему я не могу использовать std::ref в моем примере, чтобы помочь компилятору в сопоставлении функций?

1 Ответ

2 голосов
/ 18 января 2020

Теперь, когда вы знаете, что передача по значению и ссылке неоднозначны, когда разрешение перегрузки пытается сравнить их для выбора наилучшей жизнеспособной функции, давайте ответим, как бы вы использовали std::ref (или std::cref), чтобы различать передачу по значение и передача по ссылке.

Оказывается ... довольно просто. Просто запишите перегрузки так, что один принимает int, а другой принимает std::reference_wrapper<int>:

#include <functional>
#include <iostream>

void foo(int x) {
    std::cout << "Passed by value.\n";
}

void foo(std::reference_wrapper<int> x) {
    std::cout << "Passed by reference.\n";
    int& ref_x = x;
    ref_x = 42;
    /*  Do whatever you want with ref_x.  */
}

int main() {
    int x = 0;
    foo(x);
    foo(std::ref(x));
    std::cout << x << "\n";
    return 0;
}

Вывод:

Передано по значению.

Передано по ссылке.

42

Функция по умолчанию передает аргумент по значению. Если вы хотите передать по ссылке, явно используйте std::ref.

Теперь давайте ответим на ваш второй вопрос: как std::bind справляется с этим типом сценария. Вот простая демонстрация, которую я создал:

#include <functional>
#include <type_traits>
#include <iostream>

template <typename T>
struct Storage {
    T data;
};

template <typename T>
struct unwrap_reference {
    using type = T;
};

template <typename T>
struct unwrap_reference<std::reference_wrapper<T>> {
    using type = std::add_lvalue_reference_t<T>;
};

template <typename T>
using transform_to_storage_type = Storage<typename unwrap_reference<std::decay_t<T>>::type>;

template <typename T>
auto make_storage(T&& obj) -> transform_to_storage_type<T> {
    return transform_to_storage_type<T> { std::forward<T>(obj) };
}

int main() {
    int a = 0, b = 0, c = 0;
    auto storage_a = make_storage(a);
    auto storage_b = make_storage(std::ref(b));
    auto storage_c = make_storage(std::cref(c));

    storage_a.data = 42;
    storage_b.data = 42;
    // storage_c.data = 42;         // Compile error: Cannot modify const.

    // 0 42 0
    std::cout << a << " " << b << " " << c << "\n";

    return 0;
}

Это не std::bind, но используемый метод аналогичен (он также похож на std::make_tuple, который имеет тот же semanti c). make_storage по умолчанию копирует параметр, если вы явно не используете std::ref.

Как видите, std::ref не является магией c. Вам нужно сделать что-то дополнительное, чтобы это работало, что в нашем случае - сначала убрать тип (все ссылки удаляются в этом процессе), а затем проверить, является ли окончательный тип reference_wrapper или нет; если это так, разверните его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...