我知道为一个const左值引用分配一个rvalue会延长临时值的生命周期,直到范围结束.但是,我不清楚何时使用它以及何时依赖返回值优化.
LargeObject lofactory( ... ) {
// construct a LargeObject in a way that is OK for RVO/NRVO
}
int main() {
const LargeObject& mylo1 = lofactory( ... ); // using const&
LargeObject mylo2 = lofactory( ... ); // same as above because of RVO/NRVO ?
}
Run Code Online (Sandbox Code Playgroud)
根据Scot Meyers的"更有效的C++"(第20项),编译器可以优化第二种方法来构建适当的对象(这将是理想的,并且正是人们试图用const&第一种方法实现的).
const&临时工具以及何时依赖RVO/NRVO?const&方法比不使用该方法更糟糕的情况?(我正在考虑关于C++ 11移动语义的例子,如果LargeObject实现了那些......)c++ optimization const-reference temporary-objects return-value-optimization
我想检查一下我对此事的理解和结论.
在IRC上,有人问:
绑定到临时对象
const_cast的const引用是否可以接受?
翻译:他有一个临时的ref-to-const,他想抛弃它const来修改它.
我的回答是我之前曾问过一个类似的问题,其中的共识似乎是临时性本身并不是天生的const,因此你可以抛弃const你对它们的引用的性质,并通过结果修改它们.而且,只要原始参考const仍然存在,这不会影响临时的生命周期.
那是:
int main()
{
const int& x = int(3);
int& y = const_cast<int&>(x);
y = 4;
cout << x;
}
// Output: 4
// ^ Legal and safe
Run Code Online (Sandbox Code Playgroud)
我对吗?
(当然,这些代码是否真的是可取的完全是另一回事!)
这是我之前的问题的后续问题,其中明显的共识是,prvalues的cv资格处理的变化只是一个相当小的和无关紧要的变化,旨在解决一些不一致性(例如返回prvalues并通过cv-qualified返回声明的函数)类型).
但是,我看到标准中的另一个地方似乎依赖于具有cv限定类型的constprvalues:通过临时实现转换初始化带有prvalues 的引用.有关措辞可在9.3.3/5中的多个地点找到
[...]如果转换的初始化程序是prvalue,则将其类型T4调整为键入"cv1 T4"([conv.qual])并应用临时实现转换([conv.rval])[...]
[...]否则,初始化表达式被隐式转换为"cv1 T1"类型的prvalue.应用临时实现转换,并将引用绑定到结果.
目的很明显是当我们进行实际的临时物化转换时
7.3.4临时实现转换
1类型T的prvalue可以转换为类型T的xvalue.此转换通过使用临时对象评估prvalue,从prvalue初始化类型为T的临时对象([class.temporary])它的结果对象,并生成一个表示临时对象的xvalue.[...]
T它作为输入接收的类型包括所需的cv资格.
但是在非类非数组prvalue的情况下,该cv资格如何在7.2.2/2中存活?
7.2.2类型
2如果prvalue最初具有类型"cv T",其中T是cv非限定的非类非数组类型,则在进行任何进一步分析之前将表达式的类型调整为T.
或者是吗?
例如,我们在这个例子中得到了什么样的临时性
const int &r = 42;
Run Code Online (Sandbox Code Playgroud)
暂时const与否?我们能做到吗
const_cast<int &>(r) = 101; // Undefined or not?
Run Code Online (Sandbox Code Playgroud)
没有触发未定义的行为?如果我没有弄错的话,最初的意图是const int在这种情况下获得临时性.它仍然是真的吗?(对于班级类型,答案很清楚 - 我们得到一个const临时的.)
我相信表达式T()创建了一个rvalue(由标准).但是,以下代码编译(至少在gcc4.0上):
class T {};
int main()
{
T() = T();
}
Run Code Online (Sandbox Code Playgroud)
我在技术上知道这是可能的,因为成员函数可以在temporaries上调用,而上面只是调用operator =在从第一个创建的rvalue临时T().
但从概念上讲,这就像为rvalue分配一个新值.是否允许这样做有充分的理由吗?
编辑:我发现这个奇怪的原因是它在内置类型上被严格禁止,但允许在用户定义的类型上使用.例如,int(2) = int(3)不会编译,因为这是"赋值中的无效左值".
所以我想真正的问题是,这种有点不一致的行为是否构成了语言的原因?还是出于某种历史原因?(例如,在rvalue表达式上只允许调用const成员函数在概念上会更健全,但是这可能无法完成,因为这可能会破坏一些现有代码.)
c++ language-design operator-overloading rvalue temporary-objects
在回答了一些问题之后,我今天构建了这个实验
struct A {
bool &b;
A(bool &b):b(b) { }
~A() { std::cout << b; }
bool yield() { return true; }
};
bool b = A(b).yield();
int main() { }
Run Code Online (Sandbox Code Playgroud)
bfalse在true通过动态初始化将其设置为有值之前(由初始化为零).如果临时在b完成初始化之前被销毁,我们将打印false,否则true.
该规范说明临时在完整表达结束时被销毁.这似乎没有与初始化一起订购b.所以我想知道
false和true不同的运行?false用于上述的Clang打印,而GCC打印true.这让我很困惑.我是否错过了定义订单的规范文本?
c++ initialization language-lawyer temporary-objects order-of-execution
我试图理解 C++ 中的生命周期延长保证。有人可以解释为什么下面不同类型括号的使用在调用临时对象析构函数时会产生不同的结果吗?
#include <iostream>
struct X {
X() {
std::cout << __PRETTY_FUNCTION__ <<"\n";
}
~X() {
std::cout << __PRETTY_FUNCTION__ <<"\n";
}
};
struct Y {
X &&y;
};
int main() {
Y y1(X{});
std::cout << "Here1\n";
Y y2{X{}};
std::cout << "Here2\n";
}
Run Code Online (Sandbox Code Playgroud)
输出
X::X()
X::~X()
Here1
X::X()
Here2
X::~X()
Run Code Online (Sandbox Code Playgroud) 我想迭代一个临时的valarray,但它不起作用.这是我的(非工作)代码:
#include <iostream>
#include <valarray>
int main()
{
using namespace std;
valarray<int> numerators = {99, 26, 25};
valarray<int> denominators = {9, 2, 5};
for (int i : numerators / denominators) { cout << i << ","; }
// lots of errors
return 0;
}
Run Code Online (Sandbox Code Playgroud)
下面是我想要实现的最小工作示例,除了我不想定义像这样的对象temp_array.
#include <iostream>
#include <valarray>
int main()
{
using namespace std;
valarray<int> numerators = {99, 26, 25};
valarray<int> denominators = {9, 2, 5};
valarray<int> && temp_array = numerators / denominators;
for (int i : …Run Code Online (Sandbox Code Playgroud) cppreference.com和C++ 11标准使用的语言之间存在细微差别,关于临时对象的生命何时延长(强调我的).
每当引用绑定到临时或临时的基础子对象时,临时的生命周期将延长以匹配引用的生命周期,
来自C++ 11标准:
第二个上下文是引用绑定到临时的.绑定引用的临时对象或绑定引用的子对象的完整对象的临时对象在引用的生命周期内持续存在,除了:
根据标准,主题可以是成员主题,基类主题或数组元素.
如果我们严格按照以下示例代码中的标准的措辞
struct Foo
{
Foo() : a(10), b(20) {}
~Foo() { std::cout << "In Foo::~Foo()\n"; }
int a;
int b;
};
Foo getFoo()
{
return Foo();
}
void testFoo1()
{
int const& r = getFoo().a;
std::cout << "In testFoo1()\n";
(void)r; // Shut up the compiler
}
Run Code Online (Sandbox Code Playgroud)
返回的对象getFoo()的生命周期应该延长参考的生命周期.但是,一个简单的测试似乎表明它没有.
标准使用的措辞是否有缺陷?
编译器是否不符合要求?
cppreference.com使用的措词是否有缺陷?
更新
cppreference.com上使用的语言已更新为(强调我的):
每当引用绑定到临时或子对象时 …
1)返回对临时的引用是否是未定义的行为,即使该引用未被使用?例如,该程序是否保证输出"良好":
int& func()
{
int i = 5;
return i;
}
int main()
{
func();
cout << "good" << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
2)仅仅具有对不再存在的对象的引用是不确定的行为,即使该引用未被使用?例如,该程序是否保证输出"良好":
int main()
{
int *j = new int();
int &k = *j;
delete j;
cout << "good" << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
3)结合这些是不确定的行为?
int& func()
{
int i = 5;
return i;
}
int main()
{
int& p = func();
cout << "good" << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud) 你好 stackoverflow 社区,
我已经学习 C++ 几个月了,最近我一直在尝试掌握围绕“新”值类别、移动语义,尤其是临时物化的概念。
首先,如何解释“临时物化转换”这个词对我来说并不简单。转换部分对我来说很清楚(prvalue -> xvalue)。但在这种情况下,“临时”究竟是如何定义的呢?我曾经认为临时对象是仅存在的未命名对象 - 从语言的角度来看 - 直到评估它们所创建的表达式的最后一步。但这种概念似乎与临时对象的实际情况不符在临时物化、新价值类别等更广泛的背景下。
由于对“临时”一词缺乏明确性,导致我无法判断“临时物化”是临时物化还是临时物化。我认为是前者,但我不确定。另外:术语“临时”仅用于类类型吗?
这直接给我带来了下一个困惑:prvalues 和 xvalues 对于临时变量起什么作用?假设我有一个右值表达式,需要以必须将其转换为 xvalue 的方式进行计算,例如通过执行成员访问。究竟会发生什么?纯右值是否实际存在(在内存中或其他地方)并且纯右值是否已经是临时的?现在,“临时物化转换”被描述为“任何完整类型 T 的纯右值都可以转换为相同类型 T 的 xvalue。此转换通过使用临时对象评估纯右值,从纯右值初始化类型 T 的临时对象作为其结果对象,并生成一个表示临时对象的 xvalue”(https://en.cppreference.com/w/cpp/language/implicit_conversion)将纯右值转换为 xvalue。这段摘录让我认为纯右值是在内存或寄存器中任何地方都不存在的东西,直到它通过这种转换“具体化”为止。(另外,我不确定临时对象是否与临时对象相同。)因此,据我了解,此转换是通过对纯右值表达式进行求值来完成的,该表达式的结果是“真实”对象。然后,该对象由 xvalue 表达式表示(= 表示?)。记忆中发生了什么?右值在哪里,x值现在在哪里?
我的下一个问题是关于临时物化的某个部分的更具体的问题。在 Kris van Rens 在 YouTube ( https://www.youtube.com/watch?v=liAnuOfc66o&t=3576s ) 上大约 56:30 的演讲“Understanding valuecategories in C++”中,他展示了这张幻灯片:
根据 cppreference.com 关于临时物化的说法,数字 1 和 2 是明确的情况(1:类 pravlue 上的成员访问,2:将引用绑定到纯右值(如 std::string + 运算符中)。
不过,我对第三个不太确定。Cppreference 说:“请注意,当从相同类型的纯右值(通过直接初始化或复制初始化)初始化对象时,不会发生临时物化:此类对象是直接从初始化器初始化的。这确保了“保证复制省略”。 ” + 运算符返回纯右值。现在,这个 std::string 类型的纯右值用于初始化一个 auto(也应该解析为 std::string)变量。这听起来像是前面的 cppreference 摘录中讨论的情况。那么这里真的会出现临时实体化吗?由中间的 xvalue 表达式“表示”的对象(1 和 2)会发生什么情况?他们什么时候被摧毁?如果 + 运算符返回纯右值,它是否“存在”在某个地方?如果纯右值甚至不是真实的(物化?)对象,那么对象 auto …