【发布时间】:2020-07-06 20:48:05
【问题描述】:
我只想问为什么允许通过非类型模板传递对象/基元的内存位置,而我传统上希望在运行时分配和使用内存。我知道传递其内存的变量必须具有外部链接,但鉴于此,这是否意味着外部链接变量的内存位置始终是恒定的? 举例说明:
template<std::string *temp>
void f();
每次执行程序时,通过模板传递的内存位置会不会有所不同,因此不是编译时间常数,因为我认为非类型参数需要模板?
【问题讨论】:
我只想问为什么允许通过非类型模板传递对象/基元的内存位置,而我传统上希望在运行时分配和使用内存。我知道传递其内存的变量必须具有外部链接,但鉴于此,这是否意味着外部链接变量的内存位置始终是恒定的? 举例说明:
template<std::string *temp>
void f();
每次执行程序时,通过模板传递的内存位置会不会有所不同,因此不是编译时间常数,因为我认为非类型参数需要模板?
【问题讨论】:
这种事情是由运行时加载器执行的修复处理的,地址在运行时确实会有所不同,但是找到地址的位置总是在相对于加载的可执行映像的固定位置.
例如 ELF 图像格式(现在在 *nix 上很常见(具有重定位部分,它告诉加载器在程序中的哪些值需要以某种方式进行更改。编译器和链接器一起编写足够链接器能够在运行时生成最终值的信息。
说你有这个功能:
void Foo::Bar() { }
然后将其作为模板参数传递(非常做作):
template<typename Class>
struct invoke {
template<void (Class::*Fn)()>
void do_fn(Class * ptr ) { (ptr->*Fn)(); }
};
..
Foo f;
invoke<Foo>::do_fn<&Foo::Bar>(&f);
Foo::Bar 并且实例化将在可执行映像中的某个特定地址找到,重定位将告诉实例化的 do_fn 如何调用实际函数。并注意 do_fn 函数名称本身不需要运行时地址,名称只需要以某种方式计算,所有的 invoke::do_fn() 调用都可以解析为单个特化(每个翻译单元可能都有一个实例化,但链接器必须能够在单一定义规则下丢弃除一个之外的所有内容,并且最好是选择哪个实例化并不重要)。
【讨论】: