容器中的引用?

Mat*_*Mat 10 c++ reference

当字符串位于容器中时,我在将字符串作为 lambda 的引用传递时遇到问题。我猜当我调用该函数时它会消失(超出范围)init(),但为什么呢?然后,当我将它作为字符串引用传递时,为什么它不会消失?

#include <iostream>
#include <string>

struct Foo {
  void init(int num, const std::string& txt)
  {
    this->num = num;
    this->txt = txt;
  }

  int num;
  std::string txt;
};

int main()
{
  auto codegen1 = [](std::pair<int, const std::string&> package) -> Foo* {
    auto foo = new Foo;
    foo->init(package.first, package.second); //here string goes out of scope, exception
    return foo;
  };

  auto codegen2 = [](int num, const std::string& txt) -> Foo* {
    auto foo = new Foo;
    foo->init(num, txt);
    return foo;
  };

  auto foo = codegen1({3, "text"});
  auto foo2 = codegen2(3, "text");
  std::cout << foo->txt;
} 
Run Code Online (Sandbox Code Playgroud)

我知道正确的方法是使用const std::pair<int, std::string>&,但我想了解为什么这种方法不起作用。

小智 16

第 1 部分:乍一看,这看起来很糟糕。

"text"不是一个字符串,它是一个用于创建临时std::string对象的文字,然后用于初始化std::pair. 因此,您可能会认为该字符串在std::pair被引用时就消失了,因为该字符串仅是暂时需要的(即仅在构造之前)。

第 2 部分:但它不应该被破坏。

但是,作为表达式一部分创建的临时变量应该保证存活到当前“完整表达式”的末尾(简化为:直到分号)。

这就是为什么调用codegen2()工作正常的原因。创建一个临时对象std::string,并在调用codegen2()完成之前一直保持活动状态。

第 3 部分:然而,在这种情况下,它被破坏了。

codegen1()那么为什么在s 的情况下字符串会被过早销毁呢?"text"从到 的转换std::string不是作为子表达式发生的,而是作为使用其自己的作用域调用的单独函数的一部分发生的。

std::pair这里使用的构造函数是:

// Initializes first with std::forward<U1>(x) and second with std::forward<U2>(y).
template< class U1 = T1, class U2 = T2 >
constexpr pair( U1&& x, U2&& y );
Run Code Online (Sandbox Code Playgroud)

构造函数"text"作为参数获取,并且 的构造是在的构造函数内部std::string完成的,因此当我们从该构造函数返回时,作为该过程的一部分创建的临时变量将被清除。std::pair

有趣的是,如果该构造函数不存在,std::pair则 的基本构造函数:pair( const T1& x, const T2& y );会很好地处理这个问题。隐式转换std::string将在调用构造函数之前完成。

我们该如何解决这个问题?

有几种选择:

强制转换发生在正确的“级别”:

auto foo = codegen1({3, std::string("text")});
Run Code Online (Sandbox Code Playgroud)

实际上是同样的事情,但语法更好:

using namespace std::literals::string_literals;
auto foo = codegen1({3, "text"s});
Run Code Online (Sandbox Code Playgroud)

使用 a std::string_view,完全不需要转换:

auto codegen1 = [](std::pair<int, std::string_view> package) -> Foo* {
...
}
Run Code Online (Sandbox Code Playgroud)

不过,在您的情况下,sinceFoo将取得字符串的所有权,按值传递它并移动它显然是正确的方法(我还冒昧地清理了一下代码):

struct Foo {
  Foo(int num, std::string txt)
    : num(num)
    , txt(std::move(txt))
  {}

  int num;
  std::string txt;
};

int main()
{
  auto codegen1 = [](std::pair<int, std::string> package) {
    return std::make_unique<Foo>(package.first, std::move(package.second));
  };

  auto foo = codegen1({3, "text"});

  std::cout << foo->txt;
}
Run Code Online (Sandbox Code Playgroud)