Должен ли я объявить свою специализацию шаблона функции или достаточно определить ее? - PullRequest
1 голос
/ 06 ноября 2019

У меня есть несколько классов, которые можно проверить. Код, который реализует это, объявляет шаблон функции в заголовочном файле и специализирует его на разных исходных файлах:

// check.h
template <class T>
bool check(const T& object);
// class1.h
struct Class1 {int mass;};
// check_class1.cpp
#include "class1.h"
#include "check.h"
template <>
bool check(const Class1& object) {return object.mass < 100;}
// class2.h
struct Class2 {int price;};
// check_class2.cpp
#include "class2.h"
#include "check.h"
template <>
bool check(const Class2& object) {return object.price < 1000;}
// class3.h
struct Class3 {int x;};
... // 10 more classes which I can check

Этот кодиспользуется следующим образом:

#include "class1.h"
#include "class2.h"
#include "class3.h"
#include "check.h"

int main()
{
    Class1 object1{50};
    Class2 object2{500};
    Class3 object3{8};
    check(object1); // OK
    check(object2); // OK
    check(object3); // a link error appears here
}

Это работает довольно хорошо. Когда я добавляю другой класс Class3, который я могу проверить, мне не нужно трогать заголовочный файл, потому что он определяет очень широкий интерфейс. Если я забыл реализовать функцию check для Class3, компоновщик напомнит мне с сообщением об ошибке.

Мой вопрос: это поведение гарантировано, или мой код работает по счастливой случайности? Я использую Visual Studio.

Если я хочу специализировать свой шаблон функции, не должен ли я объявить все мои специализации в заголовочном файле?

Ответы [ 2 ]

1 голос
/ 06 ноября 2019

Я бы добавил эти декларации, чтобы они были в безопасности (ну, если я не перегружу их по какой-либо причине). Я не думаю, что закон слишком ясен в этом. Например, у нас есть

[temp.expl.spec]

6 Если шаблон, шаблон элемента или элементесли шаблон класса явно специализирован, то эта специализация должна быть объявлена ​​до первого использования этой специализации, которая может привести к неявной реализации в каждой единице перевода, в которой происходит такое использование;Диагностика не требуется. Если программа не предоставляет определения для явной специализации, и либо специализация используется таким образом, чтобы вызвать неявное создание экземпляра, либо член является функцией виртуального члена, программа не сформирована, диагностика не требуется. Неявная реализация никогда не генерируется для явной специализации, которая объявлена, но не определена.

Что, если я правильно прочитал, означает, что если явная специализация добавлена ​​к main.cpp, то она должна появиться до main. Потому что именно здесь может происходить неявная реализация. Абзац не заставляет ваш код выровнять плохо сформированный отчет о недоставке, потому что использование и явная специализация появляются в разных TU. Но это вызывает озабоченность.

С другой стороны, есть этот абзац:

[temp]

7 Шаблон функции, функция-член шаблона класса, шаблон переменной или статический член данных шаблона класса должны быть определены в каждой единице перевода, в которой он создается неявно, если только соответствующая специализация явно не создана в некоторой единице перевода;Диагностика не требуется.

Этот позволяет явно создавать экземпляр в отдельных невидимых TU. Но это не дает возможности для явных специализаций. Я не могу сказать, является ли это намеренным или упущением.

Причина, по которой это работает, скорее всего, связана с тем, как все это реализовано. Когда объявление функции создается неявно, оно генерирует символ, который, как оказалось, соответствует символу, созданному явной специализацией. Совпадение символов означает счастливый компоновщик, поэтому все строится и работает.

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

0 голосов
/ 06 ноября 2019

Вы должны объявить каждую явную специализацию перед их использованием. Но вы можете сделать это в заголовках, объявляя типы, для которых он специализирован.

// class2.h
struct Class2 {int price;};
template <class T>
bool check(const T& object);
template <>
bool check(const Class2& object)

(я все еще не понимаю, почему использование перегрузок не вариант).

...