【问题标题】:Reference at std::thread parameters参考 std::thread 参数
【发布时间】:2021-05-25 00:33:18
【问题描述】:

我有两个功能

void f(const int &x) {}
void g(int& x) {}

我可以做

int x = 0;
std::thread t1(f, x);

但我无法创建std::thread t2(g, x),在这种情况下我需要创建std::ref(x) 而不是只创建x,为什么需要?

为什么可以在没有std::cref 的情况下创建t1

【问题讨论】:

  • 你得到的错误信息到底是什么?
  • std::thread 将参数复制到函数中。 std::ref 将您的对象包装在 std::reference_wrapper 中,它授予引用语义,因此副本引用原始对象。
  • 好的,我明白了。但是为什么没有std::cref就可以成功创建t1呢?
  • 所有涉及参数的类型信息在C++中都很重要。请写出显示问题的可编译示例。从这个std::thread t1(f, x) 无法分辨,因为我们不知道fx 是什么!
  • 完整代码ideone.com/KjOIKc。当我将int 更改为任何类型时,它也有效。为什么它可以编译并且不需要通过std::cref 传递x

标签: c++ multithreading pass-by-reference stdthread pass-by-const-reference


【解决方案1】:

如果没有std::cref(),您的f() 函数将无法正常工作。

虽然f() 不打算改变x 后面的值,但这并不意味着这个引用后面的值不能在其他地方发生变异。

在这个例子中,没有std::cref(),原始int的副本被放入线程堆栈,x引用这个副本;我们看到11

另一方面,std::cref()x 仍然引用原始;我们看到12

/**
  g++ -std=c++17 -o prog_cpp prog_cpp.cpp \
      -pedantic -Wall -Wextra -Wconversion -Wno-sign-conversion \
      -g -O0 -UNDEBUG -fsanitize=address,undefined -pthread
**/

#include <iostream>
#include <thread>

using namespace std::chrono_literals;
void
f(const int &x)
{
  std::cout << "x=" << x << '\n';
  std::this_thread::sleep_for(1000ms);
  std::cout << "x=" << x << '\n';
}

int
main()
{
  int i=1;
  // std::thread th{f, i}; // copy to thread stack
  std::thread th{f, std::cref(i)}; // reference the original i
  std::this_thread::sleep_for(500ms);
  i+=1;
  th.join();
  return 0;
}

【讨论】:

  • 谢谢!可惜C++会偷偷复制对象。顺便说一句,为什么当我将 std::cref 更改为 std::ref 时这段代码有效?行为应该相同吗?
  • @АртёмГаркавый x 是对const 的引用,因此提供可变引用包装器不会造成伤害。另一方面,提供一个 const-reference 包装器,其中预期对可变对象的引用(您的问题中的g())会使编译器抱怨。
  • @АртёмГаркавый 很遗憾,C++ 会偷偷复制对象 “隐身”和“对 C++ 的基本语义了解不够”之间只有一线之隔。这些语义不是不言自明的,尽管它们确实出现在语言的设计过程中,并且通常是一种特殊的方式,因为否则“简单”的东西会破坏或变得不那么明显。这就是熟练使用 C++ 的意义:这种理解是有效使用该语言所固有的。无论是哪种编程语言,很多东西对于相对新手来说都是隐秘的。
猜你喜欢
  • 2021-07-12
  • 2012-10-25
  • 1970-01-01
  • 1970-01-01
  • 2014-03-05
  • 2020-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多