临时在访问()调用中?

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)

然后我没有任何警告,它“有效”......

Ted*_*gmo 5

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 起)。