【问题标题】:Strange template class conversion through the base class pointer通过基类指针进行奇怪的模板类转换
【发布时间】:2021-09-23 05:08:25
【问题描述】:

为什么这段代码在 C++ 中有效:

class Base
{
public:
    Base() = default;

    // base class stuff...
};

template<typename NumericType>
class Numeric : public Base
{
public:
    Numeric() : m_value() {}

    void setValue(NumericType value) { m_value = value; }
    NumericType value() const { return m_value; }

private:
    NumericType m_value;
};

int main()
{
    auto integerPtr = new Numeric<int>();
    Base *basePtr = integerPtr;

    if (auto doublePtr = static_cast<Numeric<double>*>(basePtr)) {
        doublePtr->setValue(6543423.634234);
        std::cout << "Wow: " << doublePtr->value() << std::endl;
    }

    return 0;
}

老实说,我期待编译错误,或者至少 static_cast 会失败,但是这个代码示例可以编译、运行,甚至可以正常工作。

但是我想知道如果我们尝试设置一个大于需要超过 4 字节内存的值,这是否会导致未定义的行为(因为该对象专用于 int 类型(通常为 4 字节)但我正在尝试存储双)。

使用 MCVS2019 32 位编译。

【问题讨论】:

    标签: c++ templates type-conversion


    【解决方案1】:

    static_cast from cppreference:

    1. 如果 new_type 是指向某个类 D 的引用或指针,并且表达式是其非虚拟基 B 的左值或指向它的纯右值指针,则 static_cast 执行向下转换。 (如果 B 是模棱两可的、不可访问的或 D 的虚拟基(或虚拟基的基),则这种向下转换是格式错误的。) 这种向下转换不进行运行时检查以确保对象的运行时类型实际上是 D , 并且只有在通过其他方式保证此前提条件时才能安全使用,例如在实现静态多态性时。可以使用 dynamic_cast 进行安全向下转换。

    您的代码调用了未定义的行为,因为强制转换格式不正确。您应该使用dynamic_cast 进行向上/向下转换。在Base 中提供一个虚拟析构函数然后这段代码:

    #include <iostream>
    
    class Base
    {
    public:
        Base() = default;
        virtual ~Base() = default;
    };
    
    template<typename NumericType>
    class Numeric : public Base
    {
    public:
        Numeric() : m_value() {}
    
        void setValue(NumericType value) { m_value = value; }
        NumericType value() const { return m_value; }
    
    private:
        NumericType m_value;
    };
    
    int main()
    {
        auto integerPtr = new Numeric<int>();
        Base *basePtr = integerPtr;
    
        if (auto doublePtr = dynamic_cast<Numeric<double>*>(basePtr)) {
            doublePtr->setValue(6543423.634234);
            std::cout << "Wow: " << doublePtr->value() << std::endl;
        }
    
        return 0;
    }
    

    是正确的,不会产生预期的输出。

    为什么这段代码在 C++ 中有效

    这不是有效的代码。请记住,不正确的代码不一定会产生编译器错误。当您的代码具有未定义的行为时,任何事情都可能发生。

    PS:我原以为您的代码会在启用优化时崩溃。令我惊讶的是,它似乎仍然有效 (https://godbolt.org/z/czfobbfqo)。那是未定义行为的讨厌的一面。它可能会被忽视,并且只会在您交付代码并在您的客户面前进行演示后爆炸;)。

    【讨论】:

      猜你喜欢
      • 2012-10-05
      • 2014-08-13
      • 1970-01-01
      • 1970-01-01
      • 2017-08-29
      • 1970-01-01
      • 1970-01-01
      • 2012-03-22
      • 1970-01-01
      相关资源
      最近更新 更多