std::bind 是否实现了 std::ref 和 std::cref 来消除函数调用的歧义?

Did std::bind implement std::ref and std::cref to disambiguate the function call?

本文关键字:std 函数调用 歧义 是否 bind 实现 ref cref      更新时间:2023-10-16

我知道我不应该重载一个函数,因为只有参数的不同之处在于其中一个通过复制传递,另一个通过引用传递:

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来帮助编译器进行函数匹配?

既然您知道当重载解析尝试比较它们以选择最佳可行函数时,按值和引用传递是不明确的,让我们回答如何使用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(。 默认情况下,make_storage复制参数,除非您显式使用std::ref

如您所见,std::ref不是魔法。您需要做一些额外的事情才能使其工作,在我们的例子中是首先衰减类型(在此过程中删除所有引用(,然后检查最终类型是否为reference_wrapper;如果是,请打开包装。