容器中的引用?
Reference in container?
我在将字符串作为对 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;
}
我知道要走的路是使用 const std::pair<int, std::string>&
,但我想了解为什么这种方法不起作用。
第 1 部分:一眼看上去就破烂了。
"text"
不是字符串,它是用于创建临时 std::string
对象的文字,然后用于初始化 std::pair
。因此,您认为仅暂时需要的字符串(即仅在构建 std::pair
之前)在被引用时消失是有道理的。
第 2 部分:但它不应该被破坏。
但是,作为表达式的一部分创建的临时对象应该保证存在到当前“完整表达式”的末尾(简化:直到分号)。
这就是对 codegen2()
的调用正常工作的原因。一个临时的 std::string
被创建,它一直存在直到对 codegen2()
的调用完成。
第 3 部分:但在这种情况下它被破坏了。
那么为什么在 codegen1()
的情况下字符串会过早地被破坏?从 "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 );
构造函数获取 "text"
作为参数,std::string
的构造在 std::pair
构造函数的 内部 中完成,因此,当我们从该构造函数 return 中清除作为该过程的一部分创建的临时变量。
有趣的是,如果那个构造函数不存在,std::pair
的基本构造函数:pair( const T1& x, const T2& y );
可以很好地处理这个问题。到 std::string
的隐式转换将在调用构造函数之前完成。
我们如何解决这个问题?
有几种选择:
强制转换发生在正确的“级别”:
auto foo = codegen1({3, std::string("text")});
效果相同,但语法更好:
using namespace std::literals::string_literals;
auto foo = codegen1({3, "text"s});
使用 std::string_view
,完全不需要转换:
auto codegen1 = [](std::pair<int, std::string_view> package) -> Foo* {
...
}
虽然,在你的情况下,由于 Foo
将取得字符串的所有权,按值传递它并且 移动 它显然是要走的路(我还冒昧把代码清理了一下):
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;
}
我在将字符串作为对 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;
}
我知道要走的路是使用 const std::pair<int, std::string>&
,但我想了解为什么这种方法不起作用。
第 1 部分:一眼看上去就破烂了。
"text"
不是字符串,它是用于创建临时 std::string
对象的文字,然后用于初始化 std::pair
。因此,您认为仅暂时需要的字符串(即仅在构建 std::pair
之前)在被引用时消失是有道理的。
第 2 部分:但它不应该被破坏。
但是,作为表达式的一部分创建的临时对象应该保证存在到当前“完整表达式”的末尾(简化:直到分号)。
这就是对 codegen2()
的调用正常工作的原因。一个临时的 std::string
被创建,它一直存在直到对 codegen2()
的调用完成。
第 3 部分:但在这种情况下它被破坏了。
那么为什么在 codegen1()
的情况下字符串会过早地被破坏?从 "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 );
构造函数获取 "text"
作为参数,std::string
的构造在 std::pair
构造函数的 内部 中完成,因此,当我们从该构造函数 return 中清除作为该过程的一部分创建的临时变量。
有趣的是,如果那个构造函数不存在,std::pair
的基本构造函数:pair( const T1& x, const T2& y );
可以很好地处理这个问题。到 std::string
的隐式转换将在调用构造函数之前完成。
我们如何解决这个问题?
有几种选择:
强制转换发生在正确的“级别”:
auto foo = codegen1({3, std::string("text")});
效果相同,但语法更好:
using namespace std::literals::string_literals;
auto foo = codegen1({3, "text"s});
使用 std::string_view
,完全不需要转换:
auto codegen1 = [](std::pair<int, std::string_view> package) -> Foo* {
...
}
虽然,在你的情况下,由于 Foo
将取得字符串的所有权,按值传递它并且 移动 它显然是要走的路(我还冒昧把代码清理了一下):
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;
}