2016-07-07 3 views
2

Предположим, что у меня есть следующий набор файлов:Correct опережающее объявление полностью специализированных шаблонов классов

Generic.h: Сложный класс шаблона

#pragma once 

template<typename K, typename V, template<typename Key, typename Value, typename ...> typename C> 
struct GenericMap 
{ 
    C<K, V> key; 
}; 

Special.h: Define полностью специализированные версия упомянутого класса шаблонов, упрощающая простоту использования.

#pragma once 

#include "Generic.h" 
#include <string> 
#include <map> 

typedef GenericMap<std::string, int, std::map> SpecialMap; 

client.h: Клиент, который использует SpecialMap и определить вперед декларацию.

#pragma once 

class SpecialMap; // Wrong forward declaration 

struct Client { 
    Client(); 
    SpecialMap* map; 
}; 

client.cpp: Клиенты код может знать Generic.h и Special.h

#include "Client.h" 
#include "Special.h" 

Client::Client() 
{ 
    map["343"] = 2; 
} 

main.cpp:

#include <Client.h> 

int main(int argc, char**args) { 
    Client c; 
    return 0; 
} 

GenericMap представляет собой шаблонный класс, который не имеет прямого заявления , Для некоторых пользователей достаточно полной версии SpecialMap из GenericMap, где для удобства использования используется typedef.

В настоящее время Client использует внутренне SpecialMap, но заголовочный файл должен объявлять только объявление вперед для SpecialMap.

К сожалению, следующие файлы не будут компилированы. Как бы то ни было, опубликованная форвардная декларация будет достаточной. Что будет правильным?

Прошу прощения за длинные списки, но это был самый маленький нерабочий пример, который я мог придумать.

+0

Просто в сторону: я не рекомендую '#pragma once'. Используйте шаблон '#ifndef ... #define ... # endif' для переносимости. – erip

+0

'typedef' не объявляет специализацию. –

+0

Спасибо за подсказку. Моя компания использует только Visual Studio. Нет проблем с переносимостью. :-) – Aleph0

ответ

1

В комментариях вы пояснили, что на самом деле вы не ссылались на специализацию на С ++. Вы задавали вопрос только о typedef:

typedef GenericMap<std::string, int, std::map> SpecialMap; 

И это почти конец истории. Это объявляет SpecialMap как typedef, псевдоним типа. Любая единица перевода, которая должна использовать SpecialMap, должна включать определение этого типа. И только это определение. Больше ничего не нужно делать. Его не нужно объявлять каким-либо другим способом. Это псевдоним. Поиск/замена псевдонима typedef с его базовым типом дает точные точные результаты. A typedef, заявленный в одной единице перевода, отображается только в этой единицы перевода. Для других единиц перевода не существует сокращений для импорта typedef в их объем.

В вашем client.h:

#include <Special.h> 

Вот где вы определили эту typedef, и это единственный способ, чтобы тянуть в этом определении.

Однако это может быть также случай, когда typedef является частью более крупного файла заголовка, и желательно просто вставлять только typedef отдельно. Это может быть сделано с заголовочный файл, содержащий только:

#include <string> 
#include <map> 

template<typename K, typename V, 
     template<typename Key, typename Value, typename ...> 
      typename C> struct GenericMap; 

typedef GenericMap<std::string, int, std::map> SpecialMap; 

Это будет голый минимум, необходимый для определения typedef псевдоним. Все, что на самом деле нужно использовать, нужно будет #include не только этот заголовочный файл, но и ваш заголовок Generic.h, который фактически определяет класс шаблона GenericMap, который здесь только прост.

+0

Большое спасибо. Это действительно решает и разъясняет мою проблему. Я добавлю следующий код в новый заголовочный файл 'SpecialMapFwd.h'. – Aleph0

+0

Я сейчас пытаюсь выяснить, что мне делать, когда 'GenericMap' содержит typedef' typedef C map_type'. Как я могу перенаправить объявление «map_type»? – Aleph0

+0

Вы не можете переслать объявление члена класса без определения основного класса. –

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