2008-10-23 2 views
5

У меня есть следующая проблема с использованием экземпляра шаблона [*].Создание шаблона шаблона шаблона C++

файл foo.h

class Foo 
{ 
public: 
    template <typename F> 
    void func(F f) 

private: 
    int member_; 
}; 

файл foo.cc

template <typename F> 
Foo::func(F f) 
{ 
    f(member_); 
} 

файл caller.cc

Foo::func(boost::bind(&Bar::bar_func, bar_instance, _1)); 

В то время как это компилируется нормально, компоновщик жалуется на неопределенный символ:

void Foo::func<boost::_bi::bind_t...>

Как я могу создать экземпляр функциюFoo::func? Поскольку он принимает функцию как аргумент, я немного запутался. Я пытался добавить функцию создания экземпляра в foo.cc, так как я привык с регулярными без функции типов:

instantiate() 
{ 
    template<> void Foo::func<boost::function<void(int)> >(boost::function<void(int)>); 
} 

Очевидно, что это не работает. Буду признателен, если кто-то может указать мне в правильном направлении.

Спасибо!

[*] Да, я прочитал часто задаваемые вопросы о parashift.

+0

Force конкретизации с: шаблон недействительными Foo :: Func (MyFunc е); – 2008-10-23 13:39:53

ответ

5

Ответ на этот вопрос зависит от компилятора. Некоторые версии компилятора Sun C++ будут обрабатывать это автоматически, создав кэш реализаций функций шаблонов, которые будут совместно использоваться отдельными единицами перевода.

Если вы используете Visual C++ и любой другой компилятор, который не может этого сделать, вы можете также поместить определение функции в заголовок.

Не беспокойтесь о повторяющихся определениях, если заголовок включен несколькими файлами .cc. Компилятор маркирует созданные шаблоном методы со специальным атрибутом, поэтому компоновщик знает, что нужно выкидывать дубликаты вместо жалобы. Это одна из причин, почему C++ имеет «одно правило определения».

Редактировать: Указанные комментарии относятся к общему случаю, когда ваш шаблон должен быть способен связывать данные с любыми параметрами. Если вы знаете закрытый набор типов, которые будут использовать клиенты, вы можете убедиться, что они доступны, используя явное инстанцирование в файле реализации шаблона, что заставит компилятор генерировать определения для других файлов, для которых они связаны. Но в общем случае, когда ваш шаблон должен работать с типами, которые могут быть известны только клиенту, нет смысла выделять шаблон в файл заголовка и файл реализации; любой клиент должен включать обе части в любом случае. Если вы хотите изолировать клиентов от сложных зависимостей, скройте эти зависимости за нетемплитуемыми функциями, а затем вызовите их из кода шаблона.

+0

... после редактирования: результат boost :: bind - это «неопределенный» тип, поэтому явное создание шаблона в этом случае не будет хорошим решением (вы можете прочитать реализацию bind.hpp и определить реальный тип для шаблон, но затем обновления в библиотеке связывания могут разбить его, поскольку тип не является частью интерфейса). – 2009-07-07 21:03:35

1

Вы включаете foo.cc в caller.cc. Активация - это то, что происходит во время компиляции - когда компилятор видит вызов в вызывающем, он создает экземпляры шаблонов, но должен иметь полное определение.

+0

... или когда вы явно его создаете. – 2009-07-07 21:08:39

1

Я думаю, что они оба ссылаются на то, что определения функции шаблона (а не только декларации) должны быть включены в файл, в котором они используются. Функции шаблона фактически не существуют, если/пока они не используются; если вы поместите их в отдельный файл cc, тогда компилятор не знает о них в других файлах cc, если вы явно не указали #include этот файл cc в файл заголовка или файл, который их вызывает, из-за того, как парсер работает.

(Вот почему определение шаблона функции, как правило, хранятся в файлах заголовка, как описано Earwicker.)

Все яснее?

+1

На самом деле вы можете принудительно создать экземпляр без его использования. См. Ниже, где я разделяю код на три файла и создаю экземпляр без его использования. – 2008-10-23 04:52:09

+0

Ошибочно предположить, что, как и в большинстве случаев, шаблоны определены в заголовочном файле, это должно быть так. – 2009-07-07 21:08:07

+0

Если вы планируете использовать их в нескольких файлах .cpp, вы должны * определить их в файле заголовка. Я уверен, что есть некоторые трудные исключения из этого правила, но это правило по уважительной причине. – 2009-07-08 00:03:17

0

Я считаю, что Earwicker верен. Проблема с явным образом созданием экземпляра функции-члена шаблона func в этом случае заключается в том, что тип, возвращаемый boost :: bind, зависит от реализации. Это не a boost :: function. Функция boost :: function содержит boost: bind, потому что у нее есть оператор присваивания шаблона, который выводит тип правой части (результат boost :: bind). В этом конкретном использовании func в caller.cc, с этой конкретной реализацией boost, тип boost :: bind на самом деле их тип упоминается в ошибке компоновщика между < и> (то есть boost::_bi::bind_t...). Но явно создавая функцию func для этого типа, вероятно, будут иметь проблемы с переносимостью.

3

Разбиение его в файлах Как вы хотите:
Не то, чтобы я рекомендовал это. Просто показывая, что это возможно.

plop.h

#include <iostream> 
class Foo 
{ 
public: 
    Foo(): member_(15){} 


    // Note No definition of this in a header file. 
    // It is defined in plop.cpp and a single instantiation forced 
    // Without actually using it. 
    template <typename F> 
    void func(F f); 

private: 
    int member_; 
}; 


struct Bar 
{ 
    void bar_func(int val) { std::cout << val << "\n"; } 
}; 

struct Tar 
{ 
    void tar_func(int val) { std::cout << "This should not print because of specialisation of func\n";} 
}; 

Plop.cpp

#include "plop.h" 
#include <boost/bind.hpp> 
#include <iostream> 

template <typename F> 
void Foo::func(F f) 
{ 
    f(member_); 
} 

// Gnarly typedef 
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Bar, int>, boost::_bi::list2<boost::_bi::value<Bar>, boost::arg<1> (*)()> > myFunc; 

// Force the compiler to generate an instantiation of Foo::func() 
template void Foo::func<myFunc>(myFunc f); 

// Note this is not a specialization as that requires the <> after template. 
// See main.cpp for an example of specialization. 

main.cpp

#include "plop.h" 
#include <boost/bind.hpp> 
#include <iostream> 

// Gnarly typedef 
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Tar, int>, boost::_bi::list2<boost::_bi::value<Tar>, boost::arg<1> (*)()> > myTar; 

// Specialization of Foo::func() 
template<> void Foo::func<myTar>(myTar f) 
{ 
    std::cout << "Special\n"; 
} 
// Note. This is not instantiated unless it is used. 
// But because it is used in main() we get a version. 

int main(int argc,char* argv[]) 
{ 
    Foo f; 
    Bar b; 
    Tar t; 

    f.func(boost::bind(&Bar::bar_func, b, _1)); // Uses instantiation from plop.cpp 
    f.func(boost::bind(&Tar::tar_func, t, _1)); // Uses local specialization 
} 
Смежные вопросы