Оказывается, что в спецификации C ++ есть положение, которое явно запрещает специализировать класс шаблона или функцию, вложенную в класс шаблона, если вы также явно не специализируете внешний шаблон.Visual Studio не применяет это правило, поэтому возникает путаница с предыдущим примером, но g ++ определенно это делает.
Если вы хотите специализировать шаблон, ваши варианты будут либо специализироваться на внешнем шаблоне, либо каким-то образомподделать поведение специализации, отправив метод одной из двух разных реализаций на основе параметра шаблона.Я знаю, что ни один из них не очень приятен, но, к сожалению, в некоторых углах шаблонов язык выглядит странно.: - (
Один из способов, которым вы можете эмулировать поведение явной специализации, - это использовать метод, называемый диспетчеризация тегов . Идея состоит в том, что мы создадим очень простую структуру, которая выглядиткак это:
template <unsigned short N> struct Box {};
Этот тип полностью пустой. Он не предназначен для непосредственного использования, а просто является способом встраивания целого числа в систему типов. В частности, Box<3>
не являетсятот же тип, что и Box<4>
и т. д.
Затем в своем классе списка определите две функции, которые выглядят следующим образом, предпочтительно помеченные как private:
template <unsigned short N>
void doLoad(const char* file, Box<N>);
void doLoad(const char* file, Box<2>);
Эти две функции являются перегрузками одногодругой, отличающийся только их последним параметром, который является либо Box<N>
в случае шаблона, либо Box<2>
в случае без шаблона. Обратите внимание, что параметры не имеют имен. Это произвольное решение, но так какмы не планируем на самом деле читать параметры, они нам не нужны. Интуиция за этими функциями заключается в том, что эта первая функция будет универсальной реализациейИон, который будет работать для любого N
, кроме 2. Вторая версия будет содержать реализацию загрузки для случая, когда N == 2
.
Наконец, реализуем load
следующим образом:
template <typename Point>
template <unsigned short N>
void List<Point>::load(const char* file) {
doLoad(file, Box<N>());
}
Как это работает?Эта функция принимает параметр, а затем вызывает doLoad
, перенаправляя этот параметр в качестве первого аргумента и передавая временный Box<N>
в качестве второго аргумента.Если N
не два, то это вызов шаблонной версии doLoad
, которая является универсальным обработчиком.Если, с другой стороны, N
равно двум, то это вызовет не шаблонную версию doLoad
, потому что не шаблонные функции имеют приоритет над шаблонными функциями во время разрешения перегрузки.
Короче говоря,реализация load
просто становится батутом, чтобы направить вас к правильному из двух реализаций.Затем вы можете поместить логику в соответствующую функцию doLoad
, чтобы получить желаемое поведение.
Надеюсь, это поможет!