У меня есть следующие три файла в моем коде (большая часть кода удалена. Это просто, чтобы изолировать проблему).

Global.h :

//global.h
#ifndef GLOBAL_H
#define GLOBAL_H
extern const int ARRAYSIZEX;
extern const int ARRAYSIZEY;
extern const int ARRAYSIZEZ;
#endif //GLOBAL_H

Global.cpp :

//global.cpp
#include "global.h"
const int ARRAYSIZEX = 5;
const int ARRAYSIZEY = 2;
const int ARRAYSIZEZ = 4;

Основной:

//main
#include "global.h"
using namespace std;

someType mySomeTypeArray[ARRAYSIZEX][ARRAYSIZEY][ARRAYSIZEZ];

int main(int argc, char **argv)
{
//...
}

Компиляция дает мне три ошибки при объявлении mySomeTypeArray.

ошибка: привязка массива не является целочисленной константой перед токеном ']'

Я хочу сохранить мои определения глобальной переменной и размера массива в global.h / cpp для этого приложения, просто для организации, чтобы все мои параметры конфигурации были в одном месте. Как правильно добиться того, что я пытаюсь сделать?

Благодарность

3
boxcartenant 21 Авг 2018 в 02:25

4 ответа

Лучший ответ

Ваше объявление терпит неудачу, потому что размеры массива нужно оценивать во время компиляции, а ваша схема инкапсуляции фактически скрывает значения от компилятора. Это правда, потому что компиляторы работают с отдельными единицами перевода. При компиляции main.cpp ваш компилятор видит только extern const int ARRAYSIZEX благодаря выражению include, но не значение, которое видно в отдельном модуле перевода, поэтому он не может понять макет памяти.

В то время как переменные const могут использоваться в качестве размеров массива в некоторых контекстах, язык предоставляет более подходящий constexpr квалификатор, который поставляется с набором ограничений, которые обеспечивают его оценку во время компиляции и пригодность для размеров массива. Я рекомендую всегда использовать его, когда это уместно, потому что это укажет вам на ошибку в таких ситуациях, как эта. В этом случае вы получите ошибку компилятора, потому что декларация extern constexpr неверна, что намекает на правильное решение: хранить значения для констант времени компиляции непосредственно внутри заголовочного файла.

< EM> global.h

constexpr int ARRAYSIZEX = ...;
constexpr int ARRAYSIZEY = ...;
constexpr int ARRAYSIZEZ = ...;

main.cpp

#include "global.h"
someType mySomeTypeArray[ARRAYSIZEX][ARRAYSIZEY][ARRAYSIZEZ];
3
patatahooligan 29 Авг 2018 в 23:15

Проблема в том, что extern int x означает, что x определен в другом файле, но не беспокойтесь о деталях, все, что вам нужно знать, это int ". Обычно это достаточно хорошо, за исключением случаев, когда компилятор должен знать прямо здесь, а затем, что x.

Поскольку это определено в другом файле, оно не может. Этот файл должен быть скомпилирован до того, как он узнает, и результат этой компиляции из-за того, как работает C ++, не может повлиять на компиляцию этого файла.

Вам нужно объявить это как const int в заголовке, если вы хотите поделиться этими значениями. extern int не будет сокращать это.

Хотя это тривиальный пример, на самом деле нет никакой причины идти по пути extern. Просто определите значения в заголовочном файле как обычные const int.

3
tadman 20 Авг 2018 в 23:46

Размер массива должен быть указан целочисленным константным выражением. const int объект может использоваться в выражении целочисленной константы тогда и только тогда, когда он объявлен с помощью инициализатора, и этот инициализатор также является выражением целочисленной константы. Ваши ARRAYSIZE... переменные не удовлетворяют этому требованию. В main они объявлены без инициализатора. Вы не можете использовать переменные ARRAYSIZE... в качестве размеров массива в main.

Если у вас нет конкретного требования дать этим переменным внешнюю связь, просто объявите (и определите) их в заголовке как

const int ARRAYSIZEX = 5;
const int ARRAYSIZEY = 2;
const int ARRAYSIZEZ = 4;

Эти объекты будут иметь внутреннюю связь, которая отличается от того, что пытается сделать ваш оригинальный вариант.

Если вы действительно хотите дать им внешнюю связь, объявите их как inline extern const в заголовке

inline extern const int ARRAYSIZEX = 5;
inline extern const int ARRAYSIZEY = 2;
inline extern const int ARRAYSIZEZ = 4;

Поскольку inline сам по себе не позволяет const навязать внутреннюю связь, extern совершенно необязателен в этих объявлениях. А поскольку комбинацию inline const можно заменить на constexpr (как заметил @MM в комментариях), вы можете добиться того же эффекта, просто

constexpr int ARRAYSIZEX = 5;
constexpr int ARRAYSIZEY = 2;
constexpr int ARRAYSIZEZ = 4;
2
AnT 21 Авг 2018 в 00:28

Проблема в том, что ARRAYSIZEX, ARRAYSIZEY и ARRAYSIZEZ не являются константами времени компиляции. Они являются константами - поэтому их значения нельзя изменить, но их значения не известны компилятору.

В C ++ процесс компиляции состоит из 3 основных этапов.

  1. Предварительная обработка всех исходных файлов выполняется препроцессором.
  2. Компиляция для каждой единицы перевода (файл .cpp) выполняется компилятором. Для каждого модуля перевода компилятор создает объектный файл.
  3. Связывание всех объектных файлов осуществляется компоновщиком. Вывод представляет собой исполняемый файл.

В C ++ ключевое слово extern для компилятора означает, что переменная «где-то» определена. Компилятор не знает реального адреса переменной, но, поместив ключевое слово extern, он уверен, что переменная действительно существует, и компоновщик сможет найти ее адрес по имени при создании исполняемого файла.

Проблема в том, что компилятор на шаге 2 хочет создать объектный файл, но он не знает, насколько большим будет массив, потому что он не знает значения этих констант. Да, компоновщик на шаге 3, наконец, найдет их при объединении всех объектных файлов, но это слишком поздно для компилятора. Так что это порождает эту ошибку.

Решение простое. Используйте уже упомянутое ключевое слово constexpr и инициализируйте все переменные инициализаторами. Ключевое слово constexpr отмечает константы времени компиляции - константы, которые должны быть инициализированы в инициализаторах и известны компилятору.

0
Timmy_A 21 Авг 2018 в 01:52
51939692