从函数返回 std::pair<std::shared_ptr<A>、std::unique_ptr<B>&> 会导致异常

Returning an std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> from a function results in weirdness

我无法理解以下代码示例中在幕后执行的(对我来说是复杂的)机制:

#include <utility>
#include <memory>
#include <iostream>

struct A {int a;};
struct B {int b;};

std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> FuncA() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::unique_ptr<B> b = std::make_unique<B>();

    a->a = 12; b->b = 13;

    std::cout << "FuncA a: " << a.get() << std::endl;
    std::cout << "FuncA b: " << b.get() << std::endl;

    std::cout << "FuncA a.a: " << a->a << std::endl;
    std::cout << "FuncA b.b: " << b->b << std::endl;

    return {a,b};
}

void FuncC(std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> input) {
  std::cout << "FuncC a: " << input.first.get()  << std::endl;
  std::cout << "FuncC b: " << input.second.get() << std::endl;

  std::cout << "FuncC a.a: " << input.first->a  << std::endl;
  std::cout << "FuncC b.b: " << input.second->b << std::endl;
}

void FuncB() {
  auto ret = FuncA();
  
  std::cout << "FuncB a: " << ret.first.get()  << std::endl;
  std::cout << "FuncB b: " << ret.second.get() << std::endl;

  std::cout << "FuncC a.a: " << ret.first->a  << std::endl;
  std::cout << "FuncC b.b: " << ret.second->b << std::endl;

  FuncC(ret);
}

int main(){
  FuncB();
}

我用 GCC 和 Clang 编译了代码,结果相似:

FuncA a: 0xfeaec0
FuncA b: 0xfeaed0
FuncA a.a: 12
FuncA b.b: 13
FuncB a: 0xfeaec0
FuncB b: 0x7ffd1c8e4a00
FuncC a.a: 12
FuncC b.b: 479087264
FuncC a: 0xfeaec0
FuncC b: 0x406100
FuncC a.a: 12
FuncC b.b: 1449378512

很明显,std::unique_pointer引用的地址(当然还有它的值)和FuncA内部不一样,但是std::shared_pointer的地址和值没有变化.

这里发生了什么,可以做些什么(如果有的话)来使引用传递正确?

是否由于从 FuncA 返回而在 std::unique_ptr 上执行了某种形式的复制构造函数?

std::pair<std::shared_ptr<A>, std::unique_ptr<B>&> FuncA() {
    // ...
    std::unique_ptr<B> b = std::make_unique<B>();
    // ...
    return {a,b};
}

创建了一个本地 std::unique_ptr<B> 并返回对它的引用作为对中的第二个元素。这是一个悬空引用,稍后会被访问,从而给程序带来未定义的行为。