C ++ создание шаблона параметров функции - PullRequest
5 голосов
/ 23 октября 2008

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

файл 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_);
}

file 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.

Ответы [ 5 ]

5 голосов
/ 23 октября 2008

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

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

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

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

3 голосов
/ 23 октября 2008

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

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
}
1 голос
/ 23 октября 2008

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

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

Что-нибудь понятнее?

1 голос
/ 23 октября 2008

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

0 голосов
/ 23 октября 2008

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

...