右值引用的直接实例化是否定义了行为?

时间:2019-02-08 21:19:53

标签: c++ g++ rvalue-reference

以下代码使用带有-Wall的g ++ 6.3.0可以干净地编译。

#include <iostream>

class Base
{
public:
    Base(char const* base) : base_(base) {}
    void print( char const* msg ) { print( base_, msg ); }

protected:
    ~Base() = default;

private:
    char const* base_;
    virtual void print( char const*, char const* ) = 0;
};

class Drv1 : public Base
{
public:
    Drv1(char const* base, int i) : Base(base) , i_(i) {}
    ~Drv1() { std::cout << "Drv1 dtor" << std::endl; }

private:
    int     i_;

    void print( char const* base, char const* msg ) override
    {
        std::cout << base << "(" << msg << "): " << i_ << std::endl;
    }
};

class Drv2 : public Base
{
public:
    Drv2(char const* base, double d) : Base(base) , d_(d) {}
    ~Drv2() { std::cout << "Drv2 dtor" << std::endl; }

private:
    double     d_;

    void print( char const* base, char const* msg ) override
    {
        std::cout << base << "(" << msg << "): " << d_ << std::endl;
    }
};


void do_test( char const* base, char const* msg, bool int_type )
{
    Base&&   _base(int_type ? (Base&&)Drv1(base, 1) : (Base&&)Drv2(base, 2.5));

    _base.print( msg );
}

int main()
{
    do_test( "Test1", "int", true );
    do_test( "Test2", "double", false );
    return 0;
} 

运行时的输出为:

Drv1 dtor
Test1(int): 1
Drv2 dtor
Test2(double): 2.5

问题:

  1. 如果在调用虚函数之前已调用派生类析构函数,那么如何定义行为呢?如果输出实际上只是幸运的事故,那么可以使用哪些编译器选项来解决此问题?

  2. rvalue reference_base中局部变量do_test()的类型是否正确?通用(或转发)引用出现在模板的上下文中,但此处没有模板。

1 个答案:

答案 0 :(得分:3)

通过绑定对临时目录的引用来扩展临时目录的生命周期,对左值引用具有与左值引用相同的规则-除了非常量右值引用可以绑定到临时对象,而非常量左值引用不能绑定到临时对象。

该程序确实具有UB。在此表达式中:(Base&&)Drv2(base, 2.5)构造了一个临时对象,并对其进行了绑定。接下来,该引用用于在完整表达式Base&& _base(int_type ? (Base&&)Drv1(base, 1) : (Base&&)Drv2(base, 2.5));中初始化另一个引用。临时引用所引用的临时生存期不会延长_base的生存期。因此,该参考悬空了。以后访问该值具有不确定的行为。

仅当使用创建临时表的表达式直接初始化引用时,扩展临时表的寿命才有效。例如:

Base&&   _base(Drv2(base, 2.5));