Создание экземпляра шаблона функции в шаблоне C++ с параметрами шаблона функции
Вопрос
У меня следующая проблема с использованием экземпляра шаблона [*].
файл фу.х
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 FAQ lite.
Решение
Ответ на этот вопрос зависит от компилятора.Некоторые версии компилятора Sun C ++ обрабатывали бы это автоматически, создавая кэш реализаций шаблонных функций, которые были бы общими для отдельных единиц перевода.
Если вы используете Visual C ++ или любой другой компилятор, который не может этого сделать, вы также можете поместить определение функции в заголовок.
Не беспокойтесь о дублирующихся определениях, если заголовок включен в несколько файлов .cc.Компилятор помечает сгенерированные шаблоном методы специальным атрибутом, чтобы компоновщик знал, что нужно выбрасывать дубликаты вместо того, чтобы жаловаться.Это одна из причин, почему в C ++ существует "правило одного определения".
Редактировать: Приведенные выше комментарии применимы в общем случае, когда ваш шаблон должен быть способен связывать данные с любыми параметрами типа.Если вам известен закрытый набор типов, которые будут использовать клиенты, вы можете убедиться, что они доступны, используя явное создание экземпляра в файле реализации шаблона, что заставит компилятор генерировать определения для других файлов для сопоставления.Но в общем случае, когда вашему шаблону необходимо работать с типами, возможно, известными только клиенту, тогда нет особого смысла разделять шаблон на файл заголовка и файл реализации;любой клиент в любом случае должен включать обе части.Если вы хотите изолировать клиентов от сложных зависимостей, спрячьте эти зависимости за не шаблонными функциями, а затем вызывайте их из кода шаблона.
Другие советы
Разделив его на файлы, как вы хотите:
Не то чтобы я это рекомендовал.Просто показываю, что это возможно.
плюх.ч
#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
}
Включаете ли вы foo.cc в caller.cc.Создание экземпляра - это то, что происходит во время компиляции - когда компилятор видит вызов в caller, он создает экземплярные версии шаблонов, но должно быть доступно полное определение.
Я думаю, что они оба имеют в виду, что определения функций шаблона (а не только объявления) должны быть включены в файл, в котором они используются.Функции шаблона на самом деле не существуют до тех пор, пока они не будут использованы;если вы поместите их в отдельный файл cc, то компилятор не узнает о них в других файлах cc, если вы явно не #include
этот файл cc помещается либо в заголовочный файл, либо в файл, который их вызывает, из-за того, как работает анализатор.
(Вот почему определения функций шаблона обычно хранятся в файлах заголовков, как описано Earwicker.)
Стало яснее?
Я считаю, что Ушастик прав.Проблема с явным созданием экземпляра функции-члена шаблона func в этом случае заключается в том, что тип, возвращаемый boost::bind, зависит от реализации.Это так не повышение::функция.Функция boost:: может содержать a boost: привязка, потому что у него есть оператор присваивания шаблона, который выводит тип правой части (результат boost::bind).В этом конкретном использовании функции в caller.cc , с этой конкретной реализацией boost, тип boost::bind на самом деле соответствует типу, указанному в ошибке компоновщика между < и > (т.е. boost::_bi::bind_t...
).Но явное создание экземпляра функции для этого типа, вероятно, приведет к проблемам с переносимостью.