Теперь, когда вы знаете, что передача по значению и ссылке неоднозначны, когда разрешение перегрузки пытается сравнить их для выбора наилучшей жизнеспособной функции, давайте ответим, как бы вы использовали 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
или нет; если это так, разверните его.