Создание экземпляра шаблона функции в шаблоне C++ с параметрами шаблона функции

StackOverflow https://stackoverflow.com/questions/228036

Вопрос

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

файл фу.х

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...).Но явное создание экземпляра функции для этого типа, вероятно, приведет к проблемам с переносимостью.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top