为何reinterpret_cast在模板实参中无法按预期工作?
C++20非类型模板参数使用整数地址的问题
代码示例
#include <iostream> typedef struct MyStruct { int member; } crake; #define GPIOA (0x40000000) template <typename T, crake* Ptr> struct MyTemplate { void doSomething() { Ptr->member = 42; } }; int main() { crake* ptr = reinterpret_cast<crake*>(GPIOA); MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance; instance.doSomething(); return 0; }
编译错误
使用C++20编译时,MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance;行报错,错误信息:
error: ‘reinterpret_cast’ from integer to pointer 29 | MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance;
但前一行执行相同的reinterpret_cast操作可以正常编译。
需求说明
希望将#define定义的外设常量地址作为非类型模板参数,写入操作合法,且避免额外const变量。
原因分析
模板参数必须是编译期常量表达式,而C++标准明确禁止reinterpret_cast从整数转指针的操作出现在常量表达式中——哪怕转换的是编译期已知的整数。而普通代码中的reinterpret_cast属于运行期操作,不受此限制,因此前一行能通过编译。
解决方法
方法1:利用编译器扩展构造编译期指针(接近原需求写法)
GCC、Clang、MSVC等主流编译器都支持在constexpr变量中使用reinterpret_cast进行整数到指针的转换(属于编译器扩展,嵌入式场景常用),直接用宏定义的地址构造指针模板参数:
#include <iostream> typedef struct MyStruct { int member; } crake; #define GPIOA (0x40000000) // 编译期构造指针常量 constexpr crake* GPIOA_PTR = reinterpret_cast<crake*>(GPIOA); template <typename T, crake* Ptr> struct MyTemplate { void doSomething() { Ptr->member = 42; } }; int main() { crake* ptr = reinterpret_cast<crake*>(GPIOA); MyTemplate<MyStruct, GPIOA_PTR> instance; instance.doSomething(); return 0; }
如果要严格符合C++20标准,可改用std::bit_cast替代reinterpret_cast:
#include <iostream> #include <bit> typedef struct MyStruct { int member; } crake; #define GPIOA (0x40000000) constexpr crake* GPIOA_PTR = std::bit_cast<crake*>(std::uintptr_t(GPIOA)); template <typename T, crake* Ptr> struct MyTemplate { void doSomething() { Ptr->member = 42; } };
方法2:改用整数类型作为非类型模板参数(完全符合标准)
直接将宏定义的地址整数作为模板参数,在成员函数内部再转换为指针,无需额外const变量,完全符合C++标准:
#include <iostream> #include <cstdint> typedef struct MyStruct { int member; } crake; #define GPIOA (0x40000000) template <typename T, std::uintptr_t Addr> struct MyTemplate { void doSomething() { crake* Ptr = reinterpret_cast<crake*>(Addr); Ptr->member = 42; } }; int main() { crake* ptr = reinterpret_cast<crake*>(GPIOA); MyTemplate<MyStruct, GPIOA> instance; instance.doSomething(); return 0; }
内容的提问来源于stack exchange,提问作者Benjamin Graef
相关产品推荐
相关产品推荐

