'неопределенная ссылка на ...' при изменении источника, содержащего заданную функцию

У меня очень простой проект с двумя источниками и одним заголовком:

  • math_precision_io_test.cc (содержащий main). main содержит следующие строки:

    int retval = 0;
    retval = write_datum<float>(file1, &f2, "float");
    retval = write_datum<double>(file2, &d2, "double");
    
  • math_precision_io.cc, содержащий функции, которые будут использоваться, включая

    template<class T>
    int write_datum(std::ofstream & file, const T * datum, const char* descr) {
        static_assert(std::is_same<T, double>::value ||
                std::is_same<T, float>::value ||
                std::is_same<T, int>::value ||
                std::is_same<T, char>::value,
                "write_datum is not defined on this class");
    
        if (file.good()) {
            const size_t data_size = sizeof(T);
            file.write((char *) datum, data_size);
            if (descr != NULL) {
                std::cout << "Writing datum " << descr << std::endl;
            }
            return 0;
        } else {
            return 1;
        }
    }
    
  • math_precision_io.h, включенный в math_precision_io_test.cc, с (среди прочего) декларацией

    template<class T>
    int write_datum(std::ofstream & file, const T * datum, const char* descr = NULL);
    

Оба источника компилируются нормально (объекты помещаются в каталог obj/). При связывании с

g++ -g -g3 -Wall -Wunused -Wuninitialized -Wextra -fmessage-length=0 -std=gnu++11 -o math_precision_io_test obj/math_precision_io_test.o obj/math_precision_io.o 

я получил

math_precision_io_test.cc:74: undefined reference to `int write_datum<float>(std::basic_ofstream<char, std::char_traits<char> >&, float const*, char const*)'
math_precision_io_test.cc:75: undefined reference to `int write_datum<double>(std::basic_ofstream<char, std::char_traits<char> >&, double const*, char const*)'

Но если я только перемещу строки функции write_datum с math_precision_io.cc на math_precision_io_test.cc, скомпилирую и линкую, ошибка исчезнет.

Я не могу понять почему.

В чем причина ошибки и как ее исправить? (имеется в виду сохранение определения функции в math_precision_io.cc и связывание в порядке).

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

Реализация шаблона должна быть видна из заголовка, пожалуйста, переместите реализацию (желательно) в заголовок как встроенное определение. Либо так, либо вы должны явно указать, какие экземпляры шаблона будут существовать (если вы знаете, какие из них будут использоваться: en.cppreference.com/w/cpp/language/…).

N00byEdge 10.09.2018 10:58

@ N00byEdge Если вы посмотрите на первую строчку write_datum, становится ясно, что он знает, какие типы будут использоваться. Так что явное создание экземпляров кажется правильным решением.

john 10.09.2018 11:23

@john Хороший глаз! Да, явное создание экземпляра здесь звучит идеально.

N00byEdge 10.09.2018 11:25

@john - Но я думаю, мне придется реплицировать явное создание экземпляра для каждого класса, и всякий раз, когда я изменяю функцию, мне придется копировать и это. Цель шаблонов - избежать этого, оставив static_assert только выбор разрешенных классов.

sancho.s ReinstateMonicaCellio 10.09.2018 11:50

@ sancho.s Если вы не хотите использовать явное создание экземпляра, то единственная другая возможность - поместить реализацию в файл заголовка (или переделать свой код).

john 10.09.2018 12:05

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

sancho.s ReinstateMonicaCellio 10.09.2018 12:08

@ sancho.s Нет, у вас все еще есть только один шаблон, но вы явно создаете его экземпляр для каждого типа, как этот template int write_datum(std::ofstream &, const float *, const char*); template int write_datum(std::ofstream &, const int *, const char*); ... Не намного больше кода, чем прототип функции.

john 10.09.2018 12:16

@john - Отлично. Я бы добавил: 1) Явные экземпляры должны находиться в исходном файле вместе с шаблоном (а не в заголовке). 2) Если функция шаблона изменяется, эти изменения должны быть записаны только один раз (если не изменяется сам прототип, в этом случае следует изменить все экземпляры).

sancho.s ReinstateMonicaCellio 10.09.2018 13:14
0
8
59
0

Другие вопросы по теме