【问题标题】:Did std::bind implement std::ref and std::cref to disambiguate the function call?std::bind 是否实现了 std::ref 和 std::cref 来消除函数调用的歧义?
【发布时间】:2020-01-17 21:30:20
【问题描述】:

我知道我不应该重载一个函数,因为参数只有一个通过复制传递,另一个通过引用传递:

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::bind 的代码。请澄清你在说什么。我怀疑你完全误解了 std bind 和 std ref 如何交互,但我不知道你是如何误解它的。因此,请提供一个 std bind 和 std ref 以您认为的方式交互(和重载)的示例。
  • std::ref 返回std::reference_wrapper 所以仍然需要转换。 en.cppreference.com/w/cpp/utility/functional/ref
  • @Yakk-AdamNevraumont:已编辑。

标签: c++ function overloading stdbind


【解决方案1】:

既然您知道当重载解析尝试比较它们以选择最佳可行函数时,按值传递和引用传递是模棱两可的,让我们回答您如何使用std::ref(或std::cref)来区分传递-值和传递引用。

事实证明……非常简单。只需编写重载,使一个接受int,另一个接受std::reference_wrapper&lt;int&gt;

#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;如果是,请打开它。

【讨论】:

    猜你喜欢
    • 2014-01-22
    • 1970-01-01
    • 1970-01-01
    • 2021-08-08
    • 2014-12-17
    • 2020-03-12
    • 2012-09-28
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多