v.p*_*v.p 1 c++ visitors temporary-objects
我有一个关于在使用时返回对本地对象的引用的编译警告visit()
,但我无法理解为什么......
我使用以下代码(有点做作,但这是出于演示目的):
#include <iostream>
template <class... Ts> struct overloaded : Ts... {
using Ts::operator()...;
};
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
using namespace std;
class B {
public:
B(int i) :
i_(i) {}
int get_i() const {
return i_;
}
private:
int i_;
};
class D: public B {
public:
D(int i) :
B(i) {}
};
const B& as_base(const variant<B, D>& op) {
return visit(overloaded {
[](const B& b) { return static_cast<const B&>(b); },
[](const D& d) {
cout << "in as_base: &d = " << &d << "\n";
const B& b = static_cast<const B&>(d);
cout << "in as_base: &b = " << &b << "\n";
return b;
}}, op);
}
auto as_base_lambda = [](const variant<B, D>& op) {
return visit(overloaded {
[](const B& b) { return static_cast<const B&>(b); },
[](const D& d) {
cout << "in as_base_lambda: &d = " << &d << "\n";
const B& b = static_cast<const B&>(d);
cout << "in as_base_lambda: &b = " << &b << "\n";
return b;
}}, op);
};
int main() {
variant<B, D> v(d);
const B& b1 = get<1>(v);
const B& b2 = as_base(v);
const B& b3 = as_base_lambda(v);
cout << "&b1 = " << &b1 << " - i = " << b1.get_i() << "\n";
cout << "&b2 = " << &b2 << " - i = " << b2.get_i() << "\n";
cout << "&b3 = " << &b3 << " - i = " << b3.get_i() << "\n";
}
Run Code Online (Sandbox Code Playgroud)
当我编译(clang++ 17,gcc++ 13)时,我收到以下警告:
test.cpp:33:12: warning: returning reference to local temporary object [-Wreturn-stack-address]
33 | return visit(overloaded {
| ^~~~~~~~~~~~~~~~~~
34 | [](const B& b) { return static_cast<const B&>(b); },
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35 | [](const D& d) {
| ~~~~~~~~~~~~~~~~
36 | cout << "in as_base: &d = " << &d << "\n";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37 | const B& b = static_cast<const B&>(d);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
38 | cout << "in as_base: &b = " << &b << "\n";
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39 | return b;
| ~~~~~~~~~
40 | },
| ~~
41 | }, op);
Run Code Online (Sandbox Code Playgroud)
所以该as_base()
函数显然创建了一些临时对象并返回对其的引用。但是哪里?我们所说的临时性是什么?我不明白的是,看似等价的功能as_base_lambda()
却没有任何问题。
如果我仍然运行代码,我会得到:
# here is the address of the d object in the variant
&b1 = 0x7fff02fb55f8 - i = 5
# the visitors in as_base() and as_base_lambda() receive the same object (same address);
# note also that the b object has the same address as d, as expected:
in as_base: &d = 0x7fff02fb55f8
in as_base: &b = 0x7fff02fb55f8
in as_base_lambda: &d = 0x7fff02fb55f8
in as_base_lambda: &b = 0x7fff02fb55f8
# however the final object returned by as_base() is different, and apparently
# plain wrong (the value i is off):
&b2 = 0x7fff02fb5594 - i = 21880
# the final object returned by as_base_lambda() is yet again different, but its value
# is correct...
&b3 = 0x7fff02fb55d4 - i = 5
Run Code Online (Sandbox Code Playgroud)
我无法理解这里发生的事情:是 Visit() 创建了一个临时地址,并最终返回了它的地址吗?但为什么 as_base_lambda() 合法,而不是 as_base() 呢?
有趣的是,如果我修改as_lambda()
为返回一个指针:
const B* as_base(const variant<B, D>& op) {
return visit(overloaded {
[](const B& b) { return static_cast<const B*>(&b); },
[](const D& d) { return static_cast<const B*>(&b); }
}}, op);
}
Run Code Online (Sandbox Code Playgroud)
然后我没有任何警告,它“有效”......
B
除非您将类型指定为 ,否则您的ambas将会返回const B&
。Lambda 是隐式的auto
,并且auto
永远不是引用,除非您使用 显式指定它auto&
。
const B& as_base(const variant<B, D>& op) {
return visit(overloaded{[](const B& b) -> const B& { return b; }, // or auto&
[](const D& d) -> const B& { return d; }},
op);
}
Run Code Online (Sandbox Code Playgroud)
如果我修改
as_lambda()
为返回一个指针 [...] 它“有效”
是的,可以将 plainauto
推导为指针类型 - 但不能推导为引用类型 - 除非您使用auto&
/auto&&
或decltype(auto)
(C++14 起)。