关于临时对象何时被销毁,这是否有效:
FILE *f = fopen (std::string ("my_path").c_str (), "r");
Run Code Online (Sandbox Code Playgroud)
在评估调用fopen之后或之后的第一个参数后,是否会立即销毁临时值fopen.
使用以下代码进行测试:
#include <cstdio>
using namespace std;
struct A {
~A() { printf ("~A\n"); }
const char *c_str () { return "c_str"; }
};
void foo (const char *s) { printf ("%s\n", s); }
int main () {
foo (A().c_str());
printf ("after\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
得到:
c_str
~A
after
Run Code Online (Sandbox Code Playgroud)
这表示首先评估整个语句,然后销毁任何临时语句.这种排序是由标准还是特定于实现的强制要求?
#include <iostream>
using namespace std;
struct CL
{
CL()
{
cout<<"CL()"<<endl;
}
CL(const CL&)
{
cout<<"CL(const CL&)"<<endl;
}
~CL()
{
cout<<"~CL()"<<endl;
}
};
CL cl;
CL fnc()
{
return cl;
}
int main() {
cout<<"start"<<endl;
const CL& ref=static_cast<const CL&>(fnc());
//...Is "ref" valid here??
cout<<"end"<<endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
fnc()返回的临时对象的生命周期是多少?它是"ref"或临时引用static_cast(fnc())的生命周期,它在语句结束时被销毁了吗?
gcc的输出(fnc()的生命周期是"ref"的生命周期):
CL() //global object "cl"
start
CL(const CL&)
end
~CL()
~CL() //global object "cl"
Run Code Online (Sandbox Code Playgroud)
VS2013的输出(fnc()的寿命是临时参考的寿命):
CL() //global object "cl"
start
CL(const CL&)
~CL()
end
~CL() //global object "cl"
Run Code Online (Sandbox Code Playgroud)
标准是什么?
我有一个现有的功能:
void foo(const Key* key = nullptr)
{
// uses the key
}
Run Code Online (Sandbox Code Playgroud)
我想将它指向临时Key对象(即rvalue),如:
foo(&Key());
Run Code Online (Sandbox Code Playgroud)
这会导致编译错误,但是在c ++ 11/14中有一种方法可以做到这一点吗?我当然可以这样做:
Key key;
foo(&key);
Run Code Online (Sandbox Code Playgroud)
但我不需要对象Key,我只需要它在foo()和foo()中
或者我可以这样做:
foo(new Key());
Run Code Online (Sandbox Code Playgroud)
但是这个对象不会被删除.
在C++中,您可以将函数的返回值(返回值,而不是引用)绑定到const引用,代码仍然有效,因为此临时的生命周期将延长到范围结束.例如
std::string get_string() {
return "abc";
}
void f() {
const std::string& str = get_string();
std::cout << str; // valid, str is not dangling reference.
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,什么时候有用,例如何时是代码
A get_a();
const A& a = get_a();
Run Code Online (Sandbox Code Playgroud)
比代码更好
A get_a();
A a = get_a();
Run Code Online (Sandbox Code Playgroud)
以什么方式(例如更快,更小的二进制大小等)?应该是什么A,get_a以及调用后的代码get_a?
我已经手工测试了几个案例,并且在每种情况下它似乎具有相同数量的副本和移动.
让我们将这个问题限制为当前的C++标准,现代版本的编译器和构建并启用了优化(O2,O3或其他编译器的等价物)
感谢valgrind中的一些分段错误和警告,我发现这段代码不正确,并且在for-range循环中有一些悬空引用.
#include<numeric>
#include<vector>
auto f(){
std::vector<std::vector<double>> v(10, std::vector<double>(3));
iota(v[5].begin(), v[5].end(), 0);
return v;
}
int main(){
for(auto e : f()[5])
std::cout << e << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它看起来好像begin并end从临时取出,并循环丢失.
当然,一种方法是做
auto r = f()[5];
for(auto e : r)
std::cout << e << std::endl;
Run Code Online (Sandbox Code Playgroud)
但是,我想知道为什么for(auto e : f()[5])是一个错误,如果有更好的方法或某种方式设计f或甚至容器(std::vector)以避免这个陷阱.
随着迭代循环是比较明显的,为什么这个问题发生(begin和end来自不同的临时对象)
for(auto it = f()[5].begin(); it != f()[5].end(); ++it)
Run Code Online (Sandbox Code Playgroud)
但是在for-range循环中,就像在第一个例子中一样,这个错误似乎很容易.
在C++ 11标准的12.2中:
绑定引用的临时对象或绑定引用的子对象的完整对象的临时对象在引用的生命周期内持续存在,除了:
绑定到构造函数的ctor-initializer(12.6.2)中的引用成员的临时绑定将持续存在,直到构造函数退出.
函数调用(5.2.2)中的引用参数的临时绑定将持续到包含该调用的完整表达式完成为止.
函数返回语句(6.6.3)中返回值临时绑定的生命周期未扩展; 临时在return语句中的full-expression结束时被销毁.
在new-initializer(5.3.4)中对引用的临时绑定将持续到包含new-initializer的full-expression完成为止.
并且标准中有最后一个案例:
struct S {
int mi;
const std::pair<int,int>& mp;
};
S a { 1,{2,3} }; // No problem.
S* p = new S{ 1, {2,3} }; // Creates dangling reference
Run Code Online (Sandbox Code Playgroud)
对我而言,2. and 3.理解并容易达成一致.但是原因是1. and 4.什么?这个例子对我来说看起来很邪恶.
我不清楚临时对象的生命周期是通过将它绑定到?:表达式中的const引用来扩展的:
class Foo {...};
Foo *someLValue = ...;
const Foo& = someLValue ? *someLValue : Foo();
Run Code Online (Sandbox Code Playgroud)
通过将默认构造函数Foo()通过绑定到本地const ref来扩展创建临时文件的生命周期,即使绑定是有条件的吗?或者这会创建一个悬空引用,因为Foo()的临时值将在?:表达式的末尾被销毁?
我正在使用指向实现的成语实现不可变对象上的装饰器模式.基本上我的设置看起来像这样
struct Object : ObjectBase {
void doSmth() override {
impl->doSmth();
}
// this is the function I'd like to implement
Object decorateWith(std::unique_ptr<ObjectDecorator>&&);
private:
std::unique_ptr<ObjectBase> impl;
};
struct ObjectDecorator : ObjectBase {
void doSmth() override {
// do some stuff
impl->doSmth();
// do some more stuff
}
private:
std::unique_ptr<ObjectBase> impl;
};
Run Code Online (Sandbox Code Playgroud)
这里,decorateWith函数应该具有不同的行为,这取决于它是否是callen on的对象是否是临时的.如果在非临时对象上调用它,它应该返回一个新的Object实例,我必须在其中创建当前Object的深层副本并将其存储在装饰器的unique_ptr中,同时新的Object本身的impl指针指着装饰者.但是,如果在临时调用decorateWith,则只需创建一个ObjectDecorator,只需将当前对象的impl指针移动到装饰器的impl指针中,然后让对象指向新的装饰器即可.
为了阻止我需要一种方法来确定从decorateWith中调用对象是否是临时的,然后根据该检查的结果使用tag-dispatch.那可能吗?
最好的Xodion
编辑:示例调用者代码可能如下所示:
decorateWith是在非临时的上调用的
int main() {
Object x{};
// this call does not modify x so it can be reused later
Object y = x.decorateWith{std::make_unique<ObjectDecorator>()};
y.doSmth(); …Run Code Online (Sandbox Code Playgroud)如果我们想用不同的类型初始化引用,我们需要将其设置为 const (const type*),以便可以隐式生成临时对象并将引用绑定到 with。或者,我们可以使用右值引用并实现相同的[1]:
右值引用可用于延长临时对象的生命周期(注意,对 const 的左值引用也可以延长临时对象的生命周期,但不能通过它们进行修改):
[...]
样品
情况1
double x = 10;
int &ref = x; //compiler error (expected)
Run Code Online (Sandbox Code Playgroud)
案例2
double x = 10;
const int &ref = x; //ok
Run Code Online (Sandbox Code Playgroud)
案例3
double x = 10;
int &&ref = x; //ok
Run Code Online (Sandbox Code Playgroud)
如果我们尝试对 const 指针(const type* &)做同样的事情,并用非 const 指针(type*)初始化它,与我预期的不同,只有情况 2 有效。为什么情况3会导致编译器错误?为什么没有生成临时文件?
情况1
int x = 10;
int *pX = &x;
const int* &ref = pX; //compiler error (expected)
Run Code Online (Sandbox Code Playgroud)
案例2
int x = 10;
int *pX = &x;
const int* …Run Code Online (Sandbox Code Playgroud) 考虑以下代码:(https://godbolt.org/z/8W699x6q6)
int* p;
const int*&& r = static_cast<int*&&>(p);
Run Code Online (Sandbox Code Playgroud)
注意:const int*&&是指向 的指针的右值引用const int。
Clang 编译它,并r绑定到一个临时对象:
p: .quad 0
r: .quad _ZGR1r_ // r is a reference to a temporary object, otherwise this would be p
Run Code Online (Sandbox Code Playgroud)
GCC 拒绝此代码:
<source>:2:18: error: binding reference of type 'const int*&&' to 'int*' discards qualifiers
2 | const int *&&r = static_cast<int*&&>(p);
| ^~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
就我个人而言,我认为 GCC 正确地实现了CWG 2352对[dcl.init.ref] p4的更改,但我不确定我的解释是否正确。这里哪个编译器是正确的?
注意:本问题中的示例受到CWG 2018中提到的最后一行代码的启发。
注意:如果允许绑定const int*&& …
c++ const-correctness language-lawyer temporary-objects reference-binding