继承引用无法正常运行

时间:2018-09-01 02:32:59

标签: c++ templates inheritance

运行以下代码时,这涉及通过称为B的类的方法更改int的值,该类从模板化类A继承,int的值不变,而且我不明白为什么,我已经用clang干线和gcc干线测试了这两者:

#include <iostream>

template<typename T>
struct A
{
    A(T& a_num_) : a_num(a_num_) {}

    T& a_num;
};

struct B : public A<int>
{
    template<typename... Args>
    B(Args... args) : A<int>(args...) {}

    void do_something()
    {
        a_num = 1634;
    }
};

int main(void)
{
    int num = 4;

    B b {num};
    b.do_something();

    std::cout << num;

    return 0;
}

我希望能打印1634张,而不是4张。

我将错误的范围缩小到B的构造函数,因为如果我将B的构造函数更改为:

B(int& x) : A<int>(x) {}

然后显示正确的值,但是当前的构造函数也应该接收一个int,因为当我键入以下内容时:

B b {num};

然后,它应该选择Args = [int&],因为这是满足A的构造函数(采用T&的唯一方法),所以这是怎么回事?在这种情况下,a_num是什么?只是一个垃圾引用,什么都不引用,或者可能是一个临时对象?

我还试图将do_something函数重写为

A<int>::a_num = 1634

但是它仍然无法更改它。

我还注意到将b声明为:

B b {6};

虽然没有办法将PR值6绑定到l值引用,但也可以使用。

所以我的问题是,为什么B的构造函数为什么选择Args = [int]而不是Args = [int&],当将Args传递给采用T&的构造函数时,如何做到这一点?

2 个答案:

答案 0 :(得分:4)

只有当您将相应的函数参数声明为T&&并将左值传递给函数时,才能将模板类型参数推导出为引用类型。

这意味着在这种情况下,Args被推导为{int}。实例化B的构造函数后,它看起来像这样:

B(int arg) : A<int>(arg) {}

这是完全正确的,因为arg是一个左值,因此a_num_的构造函数的A参数可以绑定到它。然后,您将该引用复制到A的{​​{1}}成员,然后将其绑定到的函数参数超出范围,并且a_num变成一个悬空引用。因此,当您尝试通过a_num进行分配时的行为是不确定的。


如果您希望a_num的构造函数通过引用接受其参数,则需要将参数的类型定义为引用:

B

如果您这样做,那么tmeplate <typename... Args> B(Args&... args) : A<int>(args) {} 将是对您在b.a_num中定义的num的引用,一切都会按预期运行。

答案 1 :(得分:2)

您应更改B的构造函数,以oleg/feature/foo 作为参考Args

Args&...

现在,您的代码将按您的原意打印template<typename... Args> B(Args&... args) : A<int>(args...) {}

确认内存地址

如果要确认这些变量指向相同的内存地址,可以执行以下操作:

1634

这将打印相同的内存地址。例如:

std::cout << &num << '\n';
std::cout << &b.a_num << '\n';

使用指针

由于想法是使0x7fff5508cac8 0x7fff5508cac8 num指向相同的内存地址,因此值得一提的是使用指针的解决方案。在这种情况下,您不必更改B的构造函数,而必须将A::a_num更改为A::a_num

T*

此代码还会按照您的原始要求打印template<typename T> struct A { A(T* a_num_) : a_num(a_num_) {} T* a_num; }; struct B : public A<int> { template<typename... Args> B(Args... args) : A<int>(args...) {} void do_something() { *a_num = 1634; } }; int main(void) { int num = 4; B b {&num}; b.do_something(); std::cout << num; }

编辑:我应该记住此代码是不好的做法,绝不建议将其用于生产。局部变量的内存地址绝对不能传递,因为它们仅在范围存在于堆栈中时才有效。我们可以使用智能指针或简单地通过复制值而不是重新使用其内存地址来实现更好的代码。