2015-07-31 7 views
5

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

У меня есть простое определение шаблона класса в файле «» temp.hpp

#pragma once 

template <typename T1> 
class C 
{ 
public: 
    C (T1 c) : d_c(c) {}; 
    ~C() = default; 

    void print(); 
private: 
    T1 d_c; 
}; 

Обратите внимание, что "печать() метод объявлен, но не определен здесь. Я хочу определение в файле .cpp, и оно будет специализировано для разных типов.

Таким образом, в файле temp.cpp У меня есть определение по умолчанию метода печати()

#include "temp.hpp" 
#include <iostream> 

template<typename T1> 
void 
C<T1>::print() 
{ 
    std::cout << "Printing: " << d_c << std::endl; 
} 

с последующей специализацией класса для «поплавка» типа:

template <> 
class C <float> 
{ 
public: 
    C (float f) : d_f(f) {}; 
    ~C() = default; 

    void print() 
    { 
    std::cout << "float: " << d_f << std::endl; 
    } 

private: 
    float d_f; 
}; 

и поскольку определения находятся в файле .cpp, я должен явно указать все специализации, которые я буду использовать. Поэтому у меня есть:

template class C<int>; 
template class C<float>; 

Драйвер для моего теста выглядит в test.cpp:

#include "temp.hpp" 

int main() 
{ 
    int i = 1; 
    C<int> c_int(i); 

    float f = 1.2; 
    C<float> c_float(f); 

    c_int.print(); 
    c_float.print(); 
} 

После компиляции и компоновки этого я получаю сообщение об ошибке:

test.cpp: undefined reference to `C<float>::print()' 

Объект код для C < int> правильно сгенерирован. Я могу видеть его с помощью нм:

nm -C temp.o 
... 
0000000000000000 W C<int>::print() 
0000000000000000 W C<int>::C(int) 
0000000000000000 W C<int>::C(int) 
... 

Как я уже говорил ранее, это согласуется с НКУ и лязг поэтому я предполагаю, что есть какое-то правило языка я не понимаю здесь.

Обратите внимание, что если я добавлю использование метода print() в файл temp.cpp, тогда код будет создан, но это глупо и в моем реальном коде будет невозможно. Для этого простого теста это будет выглядеть так:

void foo() 
{ 
    C<float> s(1.3); 
    s.print(); 
} 

В реальном коде, ставший этот небольшой тест мой шаблон имеет 3 аргументов шаблона, которые сочетают в себе расшириться в около 30 перестановок коды. Есть один или два из тех, для которых мне нужна специализация, которая делает что-то другое, а другое 28 я могу оставить в покое.

Любые указатели на то, где я поступил неправильно, или ссылку на язык, почему явное создание экземпляра не должно генерировать код. Я потратил 1/2 дня на чтение всех других сообщений stackoverflow при явной инстанцировании и считаю, что правильно их использую.

+0

Таким образом, вы имеете специализацию 'C ' в 'temp.cpp', но вы хотите использовать эту специализацию в' test.cpp'? Это проблема. Специализированный тип 'C ' является другим и совершенно несвязанным типом от того, что вы получили бы, если бы вы заменили 'float' на' T1' в исходном шаблоне. В 'test.cpp' он ищет' C :: print() 'где' T1 == float', и этого не существует в вашей программе. Если вы переместите специализацию в заголовок 'temp.hpp', тогда он должен работать; специализация должна быть видна компилятору при обработке 'test.cpp'. –

+0

Спасибо за объяснение. Джейсон. В реальном коде количество кода в специализации достаточно сложное, чтобы оно было нарисовано в кучу других файлов заголовков, и я бы попал в заголовок Hades для целей компиляции. Я нашел альтернативное решение, в котором я могу хранить расширения, которые генерируют незаконный код, дальше иерархии расширения. – Jay

ответ

6

С [temp.expl.spec]:

If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required.

Мы явно специализирующийся C в temp.cpp, но в test.cpp, он не объявлен до его использования. Таким образом, ваш код плохо сформирован, никакой диагностики не требуется. Вам нужно просто переместить объявление C<float> в темп.hpp

Всегда будьте осторожны с явными специализациями. Стандарт принимает их очень серьезно:

The placement of explicit specialization declarations for function templates, class templates, [...], can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

+2

Вы не указали следующий параграф. Я разочарован :) –

+0

@ T.C. Пропустил фантастическую возможность. – Barry

+0

Спасибо за ответы, я бы не хотел, чтобы моя программа была самосожженной, мой босс был бы совершенно безумным;) Я предполагаю, что мой последующий вопрос преуспел из-за частичной специализации вместо явной специализации? Это тонко ускользнуло от меня раньше. – Jay