关于指针值的混淆是编译时constatns

时间:2015-04-25 15:13:13

标签: c++ templates pointers constexpr compile-time-constant

在C ++中,指针值可能是编译时常量。这是正确的,否则,非类型模板参数和constexpr不能使用指针。但是,据我所知,静态存储的函数和对象的地址(至少)在链接时而不是编译时是已知的。以下是一个例子:

的main.cpp

#include <iostream>

template <int* p>
void f() { std::cout << p << '\n'; }

extern int a;

int main() {
    f<&a>();
}

a.cpp

int a = 0;

我只是想知道在编译a时如何知道main.cpp的地址。我希望有人可以向我解释一下这个。

特别要考虑这个

template <int* p, int* pp>
constexpr std::size_t f() { 
  return (p + 1) == (pp + 7) ? 5 : 10; 
}

int main() {
    int arr[f<&a, &b>()] = {};
}

如何分配arr的存储空间?

加:这种机制似乎相当强大。即使我启用Randomized Base Address,也会获得正确的输出。

2 个答案:

答案 0 :(得分:4)

编译器在编译时不需要知道&a,而不需要知道函数地址的值。

可以这样想:编译器将使用&a作为参数实例化您的函数模板,并生成&#34;对象代码&#34; (以任何用于传递给链接器的格式)。对象代码看起来像(好吧它赢了,但你明白了):

func f__<funky_mangled_name_to_say_this_is_f_for_&a>__:
   reg0 <- /* linker, pls put &std::cout here */
   reg1 <- /* hey linker, stuff &a in there ok? */
   call std::basic_stream::operator<<(int*) /* linker, fun addr please? */
   [...]

如果你实例化f<b&>,假设b是另一个全局静态,编译器会做同样的事情:

func f__<funky_mangled_name_to_say_this_is_f_for_&b>__:
   reg0 <- /* linker, pls put &std::cout here */
   reg1 <- /* hey linker, stuff &b in there ok? */
   call std::basic_stream::operator<<(int*) /* linker, fun addr please? */
   [...]

当您的代码要求调用其中任何一个时:

fun foo:
   call f__<funky_mangled_name_to_say_this_is_f_for_&a>__ 
   call f__<funky_mangled_name_to_say_this_is_f_for_&b>__

要调用的确切函数是在受损函数名中编码的。 生成的代码不依赖于&a&b的运行时值。 编译器知道在运行时会有这样的东西(你告诉它),这就是它所需要的一切。它会让链接器填入空白(或者如果你未能兑现承诺,就会对你大吼大叫)。

对于你的补充,我担心我对constexpr规则不够熟悉,但是我告诉我这两个编译器将在运行时评估这个函数,根据它们,它会使得代码不符合。 (如果他们错了,那么上面的答案至少是不完整的。)

template <int* p, int* pp>
constexpr std::size_t f() { 
  return (p + 1) == (pp + 7) ? 5 : 10; 
}

int main() {
    int arr[f<&a, &b>()] = {};
}

clang 3.5 in C ++ 14标准符合模式:

$ clang++ -std=c++14 -stdlib=libc++ t.cpp -pedantic
t.cpp:10:10: warning: variable length arrays are a C99 feature [-Wvla-extension]
  int arr[f<&a, &b>()];
         ^
1 warning generated.

GCC g ++ 5.1,相同模式:

$ g++ -std=c++14 t.cpp -O3 -pedantic
t.cpp: In function 'int main()':
t.cpp:10:22: warning: ISO C++ forbids variable length array 'arr' [-Wvla]
   int arr[f<&a, &b>()];

答案 1 :(得分:1)

据我所知,静态存储和函数的变量在编译时仅作为符号/占位符存储在符号表中。当占位符被解决时,它处于链接阶段。

编译器输出机器代码,保持占位符不变。然后链接器将变量/函数的占位符替换为它们各自的内存位置。所以在这种情况下,如果你只编译main.cpp而不编译a.cpp并与之链接,你必然会遇到链接器错误,你可以在这里看到http://codepad.org/QTdJCgle(我只编译了main.cpp)< / p>