C++ 由于右值到左值的转换以及未使用“正向”而导致错误

C++ 由于右值到左值的转换以及未使用“正向”而导致错误,c++,c++11,perfect-forwarding,C++,C++11,Perfect Forwarding,下面的代码显示了windows和Linux机器中的分段错误。Parent类存储指针,但由于右值到左值的转换,Parent最终存储指针的引用。 (根据我的理解,请更正) #包括 #包括 类根{ 公众: 虚空打印()=0; }; 模板 类父:公共根{ 公众: 父(PointerType&&p):ptr(p){} void Print()覆盖{ std::cout首先,这里是发生的情况,请记住getInt返回一个右值:当您不转发ptr时,您最终调用(引用后折叠) MyFunction(int*&&&

下面的代码显示了windows和Linux机器中的分段错误。
Parent
类存储指针,但由于右值到左值的转换,
Parent
最终存储指针的引用。 (根据我的理解,请更正)

#包括
#包括
类根{
公众:
虚空打印()=0;
};
模板
类父:公共根{
公众:
父(PointerType&&p):ptr(p){}
void Print()覆盖{

std::cout首先,这里是发生的情况,请记住
getInt
返回一个右值:当您不转发
ptr
时,您最终调用(引用后折叠)

  • MyFunction(int*&&&)
  • 调用
    Child::Child(int*&)
  • 它构造并存储父级
当您转发
ptr
时,您最终调用

  • MyFunction(int*&&&)
  • 调用
    Child::Child(int*&&&)
  • 它构造并存储父级
因此,问题的表现形式是一个常见的悬空引用:
Parent::ptr
必然是一个
int*&
,它恰好绑定到由
getInt
返回的临时值。然而,解决方案比仅仅武断地应用
std::forward
要复杂一些:

转发
ptr
恰好解决了这里的问题,因为在您的示例中,您正在向
MyFunction
传递一个右值;但是您当然应该能够使用左值调用
MyFunction
,并使其正常工作,而
forward
本身并不能解决这个问题。实际的问题是实例化
Parent
以引用类型开头;在C++11中,通常通过应用

一旦解决了这个问题,第二个问题将变得明显(由于引用崩溃规则,在当前代码中被幸运所掩盖):父级构造函数中的
PointerType&&
不是转发引用,因为
PointerType
是该类型的参数,而不是构造函数。因此,传入左值时,调用的构造函数最终将是
parent::parent(int*&&)
,而这反过来又不会编译。不幸的是,C++11中的“正确”解决方案很复杂……简短的总结是,如果您想将完美的转发应用到父级
的构造函数,则该构造函数需要成为一个模板;但在旧标准中正确实现这一点既复杂又冗长,因此我将eave将此作为读者的练习,并在此处选择简单的解决方案:按价值接受并移动

这两个问题都已解决,结果如下所示:

template<typename PointerType>
class Parent : public Root {
public:
    Parent(PointerType p): ptr(std::move(p)) {}

    void Print() override {
        std::cout << "About to deref ptr\n" << *ptr << '\n';
    }

    PointerType ptr;
};

class Child {
public:
    Root* root;

    template<typename PointerType>
    Child(PointerType&& p):
        root(new Parent<typename std::decay<PointerType>::type>(
            std::forward<PointerType>(p)
        ))
    {}
};
模板
类父:公共根{
公众:
父(指针类型p):ptr(std::move(p)){
void Print()覆盖{

STD::CUT快速注释:通过声明一个构造函数用于<代码>子< /C> >实际上是阻塞移动构造函数的隐式创建,因此<代码> STD::移动(CH)无效,考虑切换到<代码> v.EnStPrimeBead(STD:OFF(PTR));< /代码>或强制生成移动构造函数(<代码>子(子& &)NoRe=默认)@fvalasiad哦..我错过了,谢谢你让我知道。但这与主要问题无关,对吗?只是要求澄清。你的代码编译和运行对我来说很好,所以我不明白你的问题是什么,也许可以告诉我们你期望什么,得到什么结果instead@fvalasiad:代码具有悬空引用,并且我发现这个问题非常清楚。谢谢你提到
std::decay
template<typename PointerType>
class Parent : public Root {
public:
    Parent(PointerType p): ptr(std::move(p)) {}

    void Print() override {
        std::cout << "About to deref ptr\n" << *ptr << '\n';
    }

    PointerType ptr;
};

class Child {
public:
    Root* root;

    template<typename PointerType>
    Child(PointerType&& p):
        root(new Parent<typename std::decay<PointerType>::type>(
            std::forward<PointerType>(p)
        ))
    {}
};