英文:
Why does "reinterpret_cast" not work as expected on template arguments?
问题
我有以下的代码示例:
#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;
}
有趣的是,我在这一行上得到了错误:
MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance;
但在之前的一行没有错误,那里我做了同样的事情,即将整数重新解释为指针。
确切的错误消息说:
error: ‘reinterpret_cast’ from integer to pointer
29 | MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance;
我的目标是将一个定义为常量值(使用 #define
)的结构体的地址作为非类型模板参数。我知道这会导致崩溃,但实际上,这个地址后面有一个外设,可以对它进行写操作。所以我假设我需要使用一个常量变量,但我想避免这样做。
英文:
I have the following code example:
#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;
}
and I compiled it with C++20.Interestingly I get an error on the line
MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance;
but not on the line before, where I do the same thing, namely reinterpret casting an integer to a pointer.
The exact error message says:
"error: ‘reinterpret_cast’ from integer to pointer
29 | MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)> instance;"
My goal is to get the address of a struct defined as a constant value (with #define
) as a non-type template parameter. I know this would crash, but in reality, there is a peripheral behind the address, and writing to it is ok. So I assume, I need to use a const variable but I wanted to avoid that.
答案1
得分: 8
模板参数如下所示:
MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)>
... 必须是常量表达式。 reinterpret_cast
不能在常量表达式中使用。
在这种情况下,允许它的问题在于您正在创建一个在编译时不存在的对象的指针,因此模板参数在某种程度上没有意义。
在编译时存在的内存与运行时内存不同。
但是,您可以将地址作为模板参数传递,并在运行时执行转换:
#include <iostream>
#include <cstddef>
struct crake {
int member;
};
inline constexpr std::uintptr_t GPIOA = 0x40000000;
template <typename T, std::uintptr_t Address>
struct MyTemplate {
static inline crake * const Ptr = reinterpret_cast<crake*>(Address);
void doSomething() {
Ptr->member = 42;
}
};
int main() {
MyTemplate<crake, GPIOA> instance;
instance.doSomething();
return 0;
}
与原始代码相比,这没有额外的开销,并编译为:
main:
mov dword ptr [0x40000000], 42
xor eax, eax
ret
英文:
Template arguments like in:
MyTemplate<MyStruct, reinterpret_cast<crake*>(GPIOA)>
... have to be constant expressions. reinterpret_cast
cannot be used in constant expressions.
In this case, the problem with allowing it would be that you are creating a pointer to an object that doesn't exist at compile time, so the template argument is somewhat meaningless.
The memory which exists at compile time is different from the memory at runtime.
However, you can pass an address as a template argument, and perform the conversion at runtime:
#include <iostream>
#include <cstddef>
struct crake {
int member;
};
inline constexpr std::uintptr_t GPIOA = 0x40000000;
template <typename T, std::uintptr_t Address>
struct MyTemplate {
static inline crake * const Ptr = reinterpret_cast<crake*>(Address);
void doSomething() {
Ptr->member = 42;
}
};
int main() {
MyTemplate<crake, GPIOA> instance;
instance.doSomething();
return 0;
}
This has no additional overhead compared to the original code, and compiles to:
main:
mov dword ptr [0x40000000], 42
xor eax, eax
ret
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论