C ++ компилируется сверху вниз в исходном коде. За исключением шаблонов, каждый раз, когда встречается идентификатор, выполняется поиск имени . Это означает, что компилятор пытается найти достижимое объявление идентификатора в предыдущей части исходного кода.
Здесь вы впервые используете идентификатор freq_pair
в параметре функции внутри
int freq_sort(unsigned char* source, struct freq_pair* target);
Поскольку он не был объявлен заранее, компилятор еще не знает, каким должен быть freq_pair
. Обычно это приводит к ошибке, говорящей о том, что freq_pair
не объявлено. Однако ключевое слово struct
в основном говорит компилятору: "freq_pair
является типом класса, и если вы не найдете такого типа класса, объявите его здесь. "
Поэтомуfreq_pair
будет объявлено, но вопрос будет , где именно (т. Е. В котором scope ) будет объявлено. Он может быть объявлен как вложенный класс (struct
и class
оба представляют классы, в C ++ нет различия между этими двумя в отношении идентичности типов) внутри TargaImage
, как локальный класс для функции или какглобальный класс. На самом деле последний случай имеет место, поскольку [basic.scope.pdecl] /7.2 стандарта C ++ 17 (черновик N4659) определяет (см. Также этот вопрос для аналогичногоcase):
для подробного спецификатора типа формы
идентификатор ключа класса
if [...];в противном случае, за исключением объявления друга, идентификатор объявляется в наименьшем пространстве имен или области блока, в которой содержится объявление.
подробный спецификатор типа - это спецификатор типа, которыйиспользует struct
или одно из других ключевых слов class-key , то есть именно то, что вы используете с struct freq_pair
. Объявление вашей функции находится внутри области видимости класса (которая не является ни областью имен, ни областью блоков), поэтому объявление freq_pair
не может быть размещено там. Следующая наименьшая область, содержащая class TargaImage {...};
- это глобальная область, которая равна областью имен. Таким образом, строка
int freq_sort(unsigned char* source, struct freq_pair* target);
объявляет глобальный struct freq_pair
, и тип, указанный в объявлении, является этим типом.
Тогда
struct freq_pair {
unsigned char val;
int count;
};
определяет класс freq_pair
вложено в класс TargaImage
. Это не тот же класс , что и глобальный класс, который вы объявили заранее.
Затем мы приходим к определению
int TargaImage::freq_sort(unsigned char* source, struct freq_pair* target){
return 0;
}
Здесь, потому что мы определяем функцию, котораяявляется частью TargaImage
, имя freq_pair
сначала ищется внутри TargaImage
, где мы сейчас видим определение struct freq_pair {...};
, в котором объявлено freq_pair
, вложенное в TargaImage
, т.е. 1081 *. Ключевое слово struct
не имеет никакого дальнейшего эффекта, если тип, соответствующий имени , может быть найден , поэтому freq_pair
в этом определении теперь ссылается на TargaImage::freq_pair
.
В результатеВы объявили функцию-член, принимающую указатель на глобальный ::freq_pair
, но попытались определить функцию-член, принимающую указатель на вложенную ::TargaImage::freq_pair
. Компилятор жалуется, что они не совпадают.
Чтобы решить эту проблему, удалите все ключевые слова struct
в объявлениях переменных и используйте их только для определения или явного объявления классов вперед. Как видите, использование его в качестве детализированного спецификатора типа вызывает только головные боли. Это же правило применяется и к другим разработанным спецификаторам типов, т. Е. К тем, которые начинаются с class
/ enum
/ union
.
Однако это приведет к ошибке, поскольку freq_pair
не найден вдекларация члена, как я объяснил выше. Это легко решается перемещением определения freq_pair
перед точкой использования:
class TargaImage
{
public:
//data member
struct freq_pair {
unsigned char val;
int count;
};
//function
int freq_sort(unsigned char* source, freq_pair* target);
};
int TargaImage::freq_sort(unsigned char* source, freq_pair* target){
return 0;
}
Если это невозможно по какой-либо причине, то вы можете использовать предварительное объявление, чтобы убедиться, что первый поискнаходит правильный тип (даже если он неполный в этой точке):
class TargaImage
{
public:
//explicit forward declaration
struct freq_pair;
//function
int freq_sort(unsigned char* source, freq_pair* target);
//data member
struct freq_pair {
unsigned char val;
int count;
};
};
int TargaImage::freq_sort(unsigned char* source, freq_pair* target){
return 0;
}
Также обратите внимание, что freq_pair
является вложенным классом ,не элемент данных .