Шаблон функции в пространстве имен в отдельном файле компилирует прекрасный, но компоновщик не может найти его

Эта проблема находится в определении и объявлении шаблона функции в пространстве имен, которое определяется во внешнем файле от того, где функция инстанцируют. Вот самый маленький восстанавливаемый пример, который я мог придумать. 4 файла следуют:

Объявление шаблона функции в именованном пространстве имен:

// bar.h
#include <algorithm>

namespace barspace {

  template <typename Iter>
    void DoSomething (Iter first, Iter last);

}

Определение шаблона функции в отдельном файле:

// bar.cpp
#include "bar.h"

namespace barspace {

  template <typename Iter>
    void DoSomething (Iter first, Iter last) {
      typedef typename std::iterator_traits<Iter>::value_type val_t;
      std::sort (first, last);
    }

} // namespace barspace

Заголовок для основной программы

// foo.h
#include "bar.h"
#include <vector>

Наконец, основная программа, где шаблон функции называют:

//foo.cpp
#include "foo.h"

int main () {

  std::vector<double> v_d;
  for (int i = 0; i < 10; i++) {
    v_d.push_back (i);
  }

  barspace::DoSomething (v_d.begin(), v_d.end());

  return 0;
}

Я компилирую следующим образом:

g++ -c -o bar.o bar.cpp

g++ -c -o foo.o foo.cpp

Они хорошо работают. Теперь для соединения:

g++ bar.o foo.o -o foobar

И получающаяся ошибка компилятора о неопределенной ссылке:

foo.o: In function `main':
foo.cpp:(.text+0x6e): undefined reference to `void barspace::DoSomething<__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > > >(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >)'
collect2: ld returned 1 exit status

Существует очевидная проблема с кодом, не сделанным доступный из namespace, или от bar единица компиляции.

Кроме того, когда я пытаюсь поместить определение DoSomething в bar.h заголовок, как я был бы для хитрости проблем при определении методов шаблона класса в отдельных .cpp файлах, я получаю ту же ошибку.

Можно ли пролить некоторый свет к моему компилятору, связывающему ошибку?

7
задан Shamster 16 July 2010 в 00:28
поделиться

4 ответа

Вы пытаетесь спрятать реализацию вашей шаблонизированной функции в cpp-файл, что, к сожалению, невозможно для большинства компиляторов. Шаблонные функции/классы инстанцируются при использовании, поэтому в тот момент, когда вы вызываете DoSomething, компилятору необходимо определение функции, чтобы скомпилировать ее.

Есть несколько решений.

  1. Перенесите тело функции в заголовочный файл. Раньше у вас были проблемы с этим, но я бы сказал, что это связано с чем-то другим. Это предпочтительный подход.

  2. Включите cpp-файл из foo.cpp. (дикий, но не такой необычный).

  3. Инстанцируйте шаблон для double:

// bar.cpp
#include "bar.h"

namespace barspace {

  template<>
    void DoSomething<double> (double first, double last) {
      typedef typename std::iterator_traits<double>::value_type val_t;
      std::sort (first, last);
    }

} // namespace barspace
10
ответ дан 6 December 2019 в 15:18
поделиться

Помещение определений шаблонов в отдельный исходный файл не очень хорошо поддерживается (я считаю, что единственный компилятор, который поддерживает такую ​​поддержку, - это Comeau).

Вам нужно переместить определение в файлы заголовков:

// bar.h

namespace barspace {

  template <typename Iter>
    void DoSomething (Iter first, Iter last) {
      typedef typename std::iterator_traits<Iter>::value_type val_t;
      std::sort (first, last);
    }

} // namespace barspace
2
ответ дан 6 December 2019 в 15:18
поделиться

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

1
ответ дан 6 December 2019 в 15:18
поделиться

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

  1. Определение шаблона
  2. Параметры шаблона для создания экземпляра с помощью

В вашем случае компилятор работает с двумя модулями компиляции. Он вполне может запускать их в двух разных процессах, поэтому единицы компиляции независимы друг от друга. Когда компилятор компилирует bar.cpp, он видит определение шаблона без запросов (вызовов) для конкретных экземпляров. Следовательно, компилятор не создает экземпляр шаблона; компиляция bar.cpp фактически ничего не дает.Компилятор достаточно «умен», чтобы видеть, что вам не нужен этот шаблон, и оптимизирует его до нулевых накладных расходов.

Конечно, вам нужен этот шаблон - в foo.cpp - но это другая единица компиляции, и к настоящему времени компилятор забыл все (или еще не узнал) о bar.cpp. Однако компилятор видит объявление функции-шаблона и делает обычное предположение, что если она была объявлена, то она была определена (инстанцирована) где-то еще - и ничего не говорит.

Наконец, появляется компоновщик и получает полную картину с высоты птичьего полета. Он видит, что для std :: vector :: iterator не создан экземпляр DoSomething (Iter, Iter) , и жалуется.

Есть несколько решений вашей проблемы. Лучшее решение - использовать ключевое слово export при объявлении шаблона. К сожалению, это также наихудшее решение, поскольку подавляющее большинство компиляторов не поддерживают эту стандартную функцию.

Если серьезно, лучше всего определить шаблон в файле заголовка. Не объявляйте это там, определите там. Вам не понадобится bar.cpp, если он изначально был для этого предназначен.

1
ответ дан 6 December 2019 в 15:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: