В этом случае нормально писать специализированные задания в файле 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.