来自C++,我很惊讶这段代码在Rust中是有效的:
let x = &mut String::new();
x.push_str("Hello!");
Run Code Online (Sandbox Code Playgroud)
在C++中,你不能取一个临时的地址,一个临时的不会比它出现的表达式更长.
临时居住在Rust多久了?既然x只是借用,谁是字符串的所有者?
以下代码给出了悬空指针错误。
std::vector<std::pair<std::string, int>> c;
for (auto& b : c) {
const auto& [s, i] = b;
std::string_view v = s.substr(i);
std::cout << v;
}
Run Code Online (Sandbox Code Playgroud)
我认为它b保存了对 的引用std::pair<std::string, int>,所以s应该是对pair对象中字符串的引用。为什么这会产生悬空指针错误?我在这里缺少什么?godbolt 链接: https: //godbolt.org/z/4zMvbr
鉴于此代码示例,有关传递给临时字符串的生命周期的规则是什么S.
struct S
{
// [1] S(const std::string& str) : str_{str} {}
// [2] S(S&& other) : str_{std::move(other).str} {}
const std::string& str_;
};
S a{"foo"}; // direct-initialization
auto b = S{"bar"}; // copy-initialization with rvalue
std::string foobar{"foobar"};
auto c = S{foobar}; // copy-initialization with lvalue
const std::string& baz = "baz";
auto d = S{baz}; // copy-initialization with lvalue-ref to temporary
Run Code Online (Sandbox Code Playgroud)
根据标准:
N4140 12.2 p5.1(在N4296中删除)
绑定到构造函数的ctor-initializer(12.6.2)中的引用成员的临时绑定将持续存在,直到构造函数退出.
N4296 12.6.2 p8
绑定到mem-initializer中的引用成员的临时表达式是错误的.
因此,拥有用户定义的构造函数[1]绝对不是我们想要的.它甚至应该在最新的C++ 14中形成不良(或者是它?)gcc和clang都没有警告它.
它是否随直接聚合初始化而改变?在这种情况下,我看起来,临时寿命延长了.
现在关于复制初始化,默认移动构造函数和引用成员状态[2] …
我想创建一个const对象的临时副本,并以非const方式使用它:
struct S {
S& f() { return *this; }
};
int main() {
const S a{};
S{a}.f(); // Error on this line
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用msvc(Visual Studio 2017,C++ 14),我收到此错误:
错误C2662'S&S :: f(void)':无法将'this'指针从'const S'转换为'S&'
如果我将大括号初始化更改为经典初始化,它可以工作:
S{a}.f(); // Does not work
S(a).f(); // Works
Run Code Online (Sandbox Code Playgroud)
两种变体在gcc中编译都很好.我错过了什么或这是一个编译器错误?
我有一个std :: vector,我需要通过选定的算法对某些操作进行排序,但是在剩下的时间内保持其原始状态(例如,在输入时排序的项目).
显然我可以使用std :: copy来创建一个临时向量并对其进行排序,但我想知道是否有更好的方法,可能是通过时间戳输入的项目.
干杯
我需要编写一个类,其构造函数对对象进行常量引用并将其存储在本地.
为了避免我可以预见的大多数常见错误,我只想接受对非临时错误的引用(即:引用左值).
如何编写一个仅对非临时引用的常量引用的函数?
当然,即使非临时性也可能超出范围,从而打破了我的阶级行为,但我相信通过禁止临时引用,我将避免大多数错误.
这真是一个非常愚蠢的问题,答案可能是一个简单的"不",但我会问,以防万一,因为它会很好.
我可以这样做,行为完全符合要求:
struct A { int x; };
A inc(A a) {
a.x += 1;
return a;
}
inc({ 1 });
Run Code Online (Sandbox Code Playgroud)
事实{ 1 }是一个临时的力量,它不会被重用,因为它已被无效inc()(因为使用移动构造函数 - 请纠正我,如果我错了!).
但是,如果我不记得{ 1 }应该代表的是什么,所以我为它做了一个变量,但是我仍然想强制要求它不能被使用两次(我试图让它就像一个临时的,但命名的):
A a = { 1 };
inc(a);
inc(a);
Run Code Online (Sandbox Code Playgroud)
没有参考类型的变化a将导致编译器抱怨双重使用 - 但移动构造函数已被排除,a因为它不是临时的.
有解决方案吗?
可能重复:
C++:临时参数的生命周期?
据说临时变量作为评估完整表达式的最后一步被破坏,例如
bar( foo().c_str() );
Run Code Online (Sandbox Code Playgroud)
临时指针一直存在,直到bar返回,但是为什么
baz( bar( foo().c_str() ) );
Run Code Online (Sandbox Code Playgroud)
是否它仍然存在直到bar返回,或者baz返回意味着完全表达结束在这里,编译器我检查了baz返回后的destruct对象,但我可以依赖它吗?
下面的代码说明了我的担忧:
#include <iostream>
struct O
{
~O()
{
std::cout << "~O()\n";
}
};
struct wrapper
{
O const& val;
~wrapper()
{
std::cout << "~wrapper()\n";
}
};
struct wrapperEx // with explicit ctor
{
O const& val;
explicit wrapperEx(O const& val)
: val(val)
{}
~wrapperEx()
{
std::cout << "~wrapperEx()\n";
}
};
template<class T>
T&& f(T&& t)
{
return std::forward<T>(t);
}
int main()
{
std::cout << "case 1-----------\n";
{
auto&& a = wrapper{O()};
std::cout << "end-scope\n";
}
std::cout << "case 2-----------\n";
{ …Run Code Online (Sandbox Code Playgroud) using intArray = int[];
int (&a) [4] = intArray{1, 2, 3, 4};
Run Code Online (Sandbox Code Playgroud)
这是不允许的,因为将非const左值引用绑定到临时(rvalue)是非法的.g ++ 4.9.1和clang 3.4.2都有错误; 当它编译罚款a是const合格的
int const (&a) [4] = intArray{1, 2, 3, 4};
Run Code Online (Sandbox Code Playgroud)
但是,当我这样做
int &x = intArray{1, 2, 3, 4} [1];
Run Code Online (Sandbox Code Playgroud)
两个编译器都编译好没有错误.挖这个标准(N3337草案),§5.2.1 Subscripting说:
1后缀表达式后跟方括号中的表达式是后缀表达式.其中一个表达式应具有"指向T的指针"类型,另一个表达式应具有未映射的枚举或整数类型.结果是类型"T"的左值.类型" T"应该是完全定义的对象类型.表达式E1 [E2]与*((E1)+(E2))相同(根据定义)
2 braced-init-list不能与内置的下标运算符一起使用.
如果我用1去,那么我不明白为什么标准允许构造临时数组,因为在其中下标元素会给出一个左值,即我可以从临时获得一个左值,这与临时数据的原始概念相矛盾只能是绑定到const左值引用或右值引用.
如果我选择2,那么为什么编译器在我做的时候不会抛出错误{1, 2, 3, 4}[1]?