Где определить шаблонную функцию члена класса C ++ и функторы, которые ее создают? - PullRequest
7 голосов
/ 30 ноября 2010

У меня есть класс Foo, который используется в небольшом автономном проекте. Он имеет определение класса в Foo.h с реализацией для функций-членов класса в файле реализации Foo.cpp.

Первый вопрос - одна из функций-членов класса Foo - это шаблонный метод Foo :: doSomething (), верно ли, что реализация этого метода должна появиться с объявлением функции в Foo.h?

Параметр шаблона, с которым будет создан экземпляр Foo :: doSomething (), является одним из двух типов Functor - класс CalcA и CalcB.

Должен ли я:

  • (A) поместил определение и реализацию двух классов Functor вместе в Foo.cpp (где они фактически используются реализацией других функций-членов Foo для вызова Foo :: doSomething).
  • (B) поместил определение и реализацию двух классов Functor в Foo.h.
  • (C) я должен разделить определение и реализацию двух Функторов через Foo.h и Foo.cpp, как это было бы сделано с обычным классом?

Ответы [ 4 ]

7 голосов
/ 30 ноября 2010

Сначала вы должны понять механизм шаблонов.Шаблоны не компилируются, они создаются при их использовании, а затем их экземпляр компилируется.Таким образом, компилятор должен иметь полное определение шаблона в каждом модуле, используя функцию шаблона, чтобы сначала создать их экземпляр в соответствии с переданными вами параметрами.

Для решения вашей проблемы есть три решения, но выПосмотрим, что они оба приведут к одному и тому же результату.Либо вы реализуете свои шаблоны целиком в своем заголовочном файле внутри определения класса (мы используем суффикс их с .hxx вместо .h, чтобы уточнить, что они содержат определения шаблонов):

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
  public:
   template <class T>    
   void bar(const T& t) {
      t.doSomething();
   }
 };
#endif

Или выможно вывести определение из класса, но все еще в заголовочном файле:

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}
#endif

Наконец, вы можете реализовать тела методов шаблона во внешнем файле (с той же причиной префиксом .cxx).Он будет содержать тела методов, но не будет содержать «Foo.hxx».Вместо этого это «Foo.hxx», который будет включать «Foo.cxx» после определения класса.Таким образом, когда компилятор разрешает директиву #include, он находит все определение шаблона в том же модуле, позволяя ему создать его экземпляр:

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

#include "Foo.cxx"

#endif

// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}

Выбор между этими тремя способами реализации шаблонов довольновопрос читабельности (и вкуса).
Второе и третье эквивалентны с точки зрения сгенерированного кода, но я бы предпочел не использовать файловое решение cxx, потому что оно часто приводит к глупым ошибкам, когда вы забываете инвертировать включение.

Кроме того, известные библиотеки C ++, такие как STL или Boost, предлагают свой код только в заголовочных файлах, что является признаком хорошего дизайна.Используя внешнее определение внутри заголовков, вы уточняете определение вашего класса.Вы также запрещаете компилятору автоматически включать методы, что иногда может привести к плохим результатам, согласно Хербу Саттеру http://www.gotw.ca/gotw/033.htm

7 голосов
/ 30 ноября 2010

Общее правило:

Если foo :: doSomething () используется вне foo.cpp (т. Е. Если он публичный или защищенный, обычно), он должен идти в заголовке.

Если нет, вставка в файл cpp - это совершенно нормально, и даже хорошая идея (так как это убирает беспорядок в заголовочном файле).

Итак, если функторы толькоиспользуемый в файле cpp, обязательно поместите туда функцию шаблона.Можно изменить вещи позже, если это изменится.

1 голос
/ 30 ноября 2010

Определение метода шаблона действительно должно быть в заголовочном файле того класса, к которому он принадлежит.

Как это:

class MyClass
{
    template <typename T>
    void foo(const T&)
    {
        // Definition
    }
};

Или как это (обратите внимание, что определение метода шаблона может быть включено из отдельного файла после объявления класса)

class MyClass
{
    template <typename T> void foo(const T&);
};

template <typename T>
void MyClass::foo(const T&)
{
    // Definition
}

Все остальное зависит от стиля, который вы согласовали, и ваших потребностей.

Я бы поместил объявление functor (или даже определение, если они простые) в заголовок, если я использую их не только в Foo или если Foo использует их в качестве члена класса.

1 голос
/ 30 ноября 2010

По умолчанию я бы поместил определение шаблонов функций-членов прямо в файл .h, например:

class Foo
{
public: 
  template<typename T> void DoSomething(T t);
};

// ... later...

template<typename T>
void Foo::DoSomething(T t)
{
  // ...
}

Если это неоптимально для конкретного случая, я бы принял более героические меры. Начиная с #include -ing .inc-файла с определением в конце .h-файла, или, возможно, даже делая явные экземпляры в файлах .cpp, где мне нужно было использовать шаблоны функций-членов.

...