2015-04-25 2 views
6

В C++ значения указателя могут быть константами времени компиляции. Это верно, в противном случае параметры шаблона непигового типа и constexpr не будут работать с указателями. Однако, насколько мне известно, адреса функций и объектов статического хранилища известны (по крайней мере) во время привязки, а не времени компиляции. Ниже приводится иллюстрация:Путаница о значениях указателя, являющихся константами компиляции-времени

main.cpp

#include <iostream> 

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

extern int a; 

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

a.cpp

Я просто интересно, как адрес 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?

PLUS: Этот механизм кажется довольно надежным. Даже когда я включил Randomized Base Address, получается правильный выход.

+3

В г ++, имя переменной, адрес которой будет принято искажаются во имя функции: http://coliru.stacked-crooked.com/a/ee352366c870c010 – dyp

+0

@dyp Это имеет смысл! – Lingxi

+0

@ dyp Компилятор в конечном итоге скорректирует значение адреса 'p' в двоичном коде экземпляра шаблона. Могу я понять это так? – Lingxi

ответ

4

компилятору не нужно знать значение из &a во время компиляции не больше, чем нужно для адреса функций.

Подумайте об этом так: компилятор будет создавать шаблон функции с &a в качестве параметра и сгенерировать «объектный код» (в любом формате, который он использует для перехода к компоновщику). Код объекта будет выглядеть (ну это не будет, но вы получите идею):

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>()] = {}; 
} 

лязг 3,5 в 14 стандартов C++ соответствующий режим:

$ 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. 

НКУ г ++ 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>()]; 
+0

Тогда как насчет этого 'template constexpr std :: size_t f() {return (p + 1) == (pp + 7)? 5: 10; } ', и я использую возвращаемое значение как оценку в определении массива? – Lingxi

+0

Затем как выделить хранилище для массива 'int arr [f <&a, &b>()] = {};'? – Lingxi

+0

Интересный вопрос. Не могу заставить его скомпилировать в режиме C++ 11, хотя (clang ++ и g ++ говорят мне, что это VLA). То же самое с режимом C++ 14 (g ++ 5.1/clang ++ 3.5) – Mat

1

Насколько я знаю, переменные статического хранилища и функции хранятся просто как символы/держатели мест в таблице символов при компиляции. Он находится на этапе связывания, когда владельцы мест разрешены.

Компилятор выдает машинный код, сохраняя местоположения неповрежденными. Затем компоновщик заменяет заполнители переменных/функций соответствующими ячейками памяти. Таким образом, в этом случае, если вы просто скомпилируете main.cpp без компиляции a.cpp и связывания с ним, вы обязательно столкнетесь с ошибкой компоновщика, как вы можете видеть здесь http://codepad.org/QTdJCgle (Я собрал только main.cpp)

 Смежные вопросы

  • Нет связанных вопросов^_^