В этом случае нормально писать специализированные задания в файле cpp?
Скажем, что мой код структурирован таким образом:
-
header1.h
template <class T, template<class> class C>
struct metafunction {
using type = typename C<T>::type;
};
inline namespace msn {
template <class T> struct implementation;
}
// uses the *implementation* not defined in the header!
template <class T>
struct use_case {
using type = typename metafunction<T, implementation>::type;
};
-
cpp1.cpp
#include <header1.h>
// I'll only need this in this compilation unit, so the
// question is: "Is this a good place to define it?"
template <>
struct implementation<int> {
using type = int;
};
int main() {
using tt = int;
// is this point of instantiation OK due to
// the existence of a specialization in the same cpp file?
using tt = use_case<int>::type;
tt var;
(void)var;
}
Мое предварительное условие состоит в том, что я буду использовать только специальную специализацию внутри файлов cpp, поэтому мне не придется иметь дело с проблемами компоновщика. Я знаю, что это не будет работать для файла cpp2.cpp
, включая header1.h
, и пытается просто использовать use_case<int>
или переопределить implementation<int>
, который нарушает ODR. Так что я спрашиваю, является ли этот код аналогичным линейной форме (версия, где все помещается в один файл cpp с соответствующий порядок), который (по-видимому) компилируется штрафом.
Ответы
Ответ 1
Да, если это используется только в пределах одной единицы перевода, это хорошо сформировано.
Имейте в виду, что эффект #include
равен: если ссылочный файл вставлен дословно в блок перевода.
Что такое #include
.
Из этого можно сделать несколько выводов:
Каждая единица перевода С++ представляет собой один виртуальный файл.
Поэтому всякая специализация в хорошо сформированной единице перевода ссылается в той же самой единицы перевода, которая ее определяет.
Q.E.D.
Ответ 2
Как правило, я согласен с ответом @Sam, но я бы сделал такую специализацию только для локальных типов. С "локальным типом" я подразумеваю тип, доступный только в этой блоке перевода, например. класс, определенный только в этом .cpp файле (в анонимном пространстве имен).
Причина в том, что при такой специализации по типу, который широко доступен, шире, чем специализация, очень легко иметь нарушение ODR (одно определение правила). Если в будущем другой код начнет использовать шаблон с тем же типом, легко понять, что вы получаете два экземпляра шаблона, которые не совпадают (один со специализацией и один без него). В этом случае это нарушение ODR, и поведение не определено.
Итак, int
он определенно не является хорошим кандидатом для "локальной" специализации.
Если вы все еще хотите локализовать специализацию в файле .cpp, возможно, вы хотите добавить дополнительный тег к параметрам шаблона (что требует добавления еще одного параметра к исходному шаблону), и этот тег будет с локальный тип (например, определение struct private_tag{};
внутри анонимного пространства имен в этом файле и использование его в специализации). Таким образом, специализация имеет уникальный тип, который не может использоваться ни в каком другом TU, поэтому вы можете быть в безопасности от нарушения ODR.