You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++模板类型间使用reinterpret_cast的行为是否为明确定义?

C++模板仿函数reinterpret_cast行为合法性问题

测试代码

#include <iostream>
using namespace std;

//--------------------Foo------------------
template<int Index>
class Foo {
 public:
  Foo(string first, string second, string third) {
    foo_[0] = first;
    foo_[1] = second;
    foo_[2] = third;
  }

  string operator()() const {
    return foo_[Index];
  }

 private:
  string foo_[3];
};

//---------------------Bar------------------
class BarBase {
 public:
  virtual string operator()() const { return "BarBase"; };
};

template<int Index>
class Bar : public BarBase {
 public:
  Bar(string first, string second, string third) {
    bar_[0] = first;
    bar_[1] = second;
    bar_[2] = third;
  }

  string operator()() const {
    return bar_[Index];
  }

 private:
  string bar_[3];
};

//---------------------Wrapper------------------
class WrapperBase {
 public:
  virtual string operator()() const { return "WrapperBase"; };
};

template<typename T>
class Wrapper : public WrapperBase {
 public:
  Wrapper(T* functor) : functor_(functor) {}
  
  string operator()() const {
    return (*functor_)();
  }
 
 private:
  T* functor_;
};

int main()
{
    Foo<0> foo0("A", "B", "C");
    Foo<1>& foo1 = *reinterpret_cast<Foo<1>*>(&foo0);
    Foo<2>& foo2 = *reinterpret_cast<Foo<2>*>(&foo0);
    cout<< "foo: " << foo1() << foo2() <<"\n";
    
    Bar<0> bar0("A", "B", "C");
    Bar<1>& bar1 = *reinterpret_cast<Bar<1>*>(&bar0);
    Bar<2>& bar2 = *reinterpret_cast<Bar<2>*>(&bar0);
    cout<< "bar: " << bar1() << bar2() <<"\n";
    
    WrapperBase* wrappedfoo0 = new Wrapper<Foo<0>>(&foo0);
    WrapperBase* wrappedfoo1 = new Wrapper<Foo<1>>(&foo1);
    WrapperBase* wrappedfoo2 = new Wrapper<Foo<2>>(&foo2);
    cout<< "wrapped foo: " << (*wrappedfoo1)() << (*wrappedfoo2)() <<"\n";

    return 0;
}

运行输出

foo: BC
bar: AA
wrapped foo: BC

原问题描述

Foo和Bar逻辑完全一致,唯一区别是Bar继承自基类且实现的operator()为虚函数,因此Bar类实例带有虚函数指针,而Foo没有。
我目前理解bar输出AA而foo输出BC的原因,若有误恳请指正:两个类各有3个模板实例,对应3个operator()实现,各自的模板参数Index是硬编码的。但由于Bar带有虚函数指针,将Bar<0>通过reinterpret_cast转换为Bar<1>后,虚函数指针仍指向Bar<0>的函数实现。
我想确认这段代码的行为是否为明确定义,尤其是"Foo"和"Wrapped Foo"场景:只要仿函数不使用继承,就可以将其reinterpret_cast为其他Foo类型,调用operator()时会使用当前变量(或是Wrapper实例化时使用的)模板参数对应的Index?

补充提问

如果移除Foo的构造函数,将foo_成员改为public并从外部初始化,情况如何?
此时Foo属于POD类型,C++标准(9.2.18)中关于reinterpret_cast和POD的说明如下:

指向POD结构体对象的指针,经reinterpret_cast适当转换后,会指向其首个成员(若该成员为位域,则指向其所在的存储单元),反之亦然。[注:为满足对齐要求,POD结构体内部可能存在未命名填充,但起始位置不会有填充。
因此若移除Foo的构造函数,Foo及Wrapped Foo场景下的行为是否为明确定义?


解答
  • 你对Bar输出AA的理解是正确的:虚函数调用依赖对象实际内存里存储的虚表指针,reinterpret_cast只是对指针的类型做了翻译,不会修改原对象的内存内容,因此不管你把Bar<0>实例转成哪一个Bar子类的引用,调用虚函数operator()始终会触发Bar<0>的实现,返回bar_[0]也就是A,最终输出AA。
  • Foo和Wrapped Foo场景的行为是未定义行为,和Foo是否带继承无关:Foo<0>和Foo<1>、Foo<2>是完全独立的不同类型,哪怕它们的内存布局看起来完全一致,C++标准也没有规定同模板不同参数的实例布局必须兼容,你通过不匹配的类型左值访问原对象,已经违反了严格别名规则,你看到输出BC只是当前编译器特定编译配置下的巧合结果,换编译环境、开优化等级都可能出现完全不符合预期的输出。
  • 补充提问场景依然是未定义行为:你提到的POD相关规则只适用于把结构体指针转换为它第一个成员的指针的场景,完全不适用于两个不同类型的结构体互相转换的情况,哪怕二者都是POD也不行,改完POD也不能让这段代码的行为变得合法。

内容的提问来源于stack exchange,提问作者matthias_buehlmann

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 08:24:06