简体   繁体   English

C ++模板实例化函数模板参数

[英]C++ template instantiation of function template parameters

I have the following problem using template instantiation [*]. 我使用模板实例化[*]有以下问题。

file foo.h 文件foo.h

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

private:
    int member_;
};

file foo.cc 文件foo.cc

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

file caller.cc file caller.cc

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

While this compiles fine, the linker complains about an undefined symbol: 虽然编译良好,但链接器会抱怨未定义的符号:

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

How can I instantiate the function Foo::func ? 如何实例化函数 Foo::func Since it takes a function as argument, I am little bit confused. 由于它需要一个函数作为参数,我有点困惑。 I tried to add an instantiation function in foo.cc , as I am used to with regular non-function types: 我试图在foo.cc中添加一个实例化函数,因为我习惯使用常规的非函数类型:

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

Obviously, this does not work. 显然,这不起作用。 I would appreciate if someone can point me in the right direction. 如果有人能指出我正确的方向,我将不胜感激。

Thanks! 谢谢!

[*] Yes, I read the parashift FAQ lite. [*]是的,我读了parashift FAQ lite。

The answer to this is compiler dependent. 答案取决于编译器。 Some versions of the Sun C++ compiler would handle this automatically by building a cache of template function implementations that would be shared across separate translation units. 某些版本的Sun C ++编译器可以通过构建模板函数实现的缓存来自动处理这些模板,这些实现将在不同的翻译单元之间共享。

If you're using Visual C++, and any other compiler that can't do this, you may as well put the function definition in the header. 如果您使用的是Visual C ++以及任何其他无法执行此操作的编译器,您也可以将函数定义放在标题中。

Don't worry about duplicate definitions if the header is included by multiple .cc files. 如果多个.cc文件包含标头,请不要担心重复定义。 The compiler marks template-generated methods with a special attribute so the linker knows to throw away duplicates instead of complaining. 编译器使用特殊属性标记模板生成的方法,因此链接器知道丢弃重复项而不是抱怨。 This is one reason why C++ has the "one definition rule". 这就是为什么C ++具有“一个定义规则”的原因之一。

Edit: The above comments apply in the general case where your template must be capable of linking given any type parameters. 编辑:以上注释适用于您的模板必须能够链接给定任何类型参数的一般情况。 If you know a closed set of types that clients will use, you can ensure they are available by using explicit instantiation in the template's implementation file, which will cause the compiler to generate definitions for other files to link against. 如果您知道客户端将使用的一组封闭类型,则可以通过在模板的实现文件中使用显式实例化来确保它们可用,这将导致编译器生成要链接的其他文件的定义。 But in the general case where your template needs to work with types possibly only known to the client, then there is little point in separating the template into a header file and and implementation file; 但是在一般情况下,您的模板需要使用可能只为客户端所知的类型,那么将模板分成头文件和实现文件几乎没有意义; any client needs to include both parts anyway. 无论如何,任何客户都需要包括这两个部分。 If you want to isolate clients from complex dependencies, hide those dependencies behind non-templated functions and then call into them from the template code. 如果要将客户端与复杂的依赖关系隔离,请隐藏非模板化函数后面的依赖关系,然后从模板代码中调用它们。

Splitting it into files Like you want: 将其拆分为文件您想要的:
Not that I recommend this. 不是我推荐这个。 Just showing that it is possible. 只是表明它是可能的。

plop.h 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 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 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
}

Are you including foo.cc into caller.cc. 你是否将foo.cc包含在caller.cc中? Instantiation is something that happens at compile time -- when the compiler sees the call in caller it makes instantiated versions of the templates but needs to have the full definition available. 实例化是在编译时发生的 - 当编译器在调用者中看到调用时,它会生成模板的实例化版本,但需要提供完整的定义。

I think what they're both referring to is that template function definitions (not just declarations) have to be included in the file where they're used. 我认为他们所指的是模板函数定义(不仅仅是声明)必须包含在它们被使用的文件中。 Template functions don't actually exist unless/until they're used; 模板函数实际上并不存在,除非/直到它们被使用; if you put them in a separate cc file, then the compiler doesn't know about them in the other cc files, unless you explicitly #include that cc file into either the header file or the file that's calling them, due to the way the parser works. 如果你把它们放在一个单独的cc文件中,那么编译器不会在其他cc文件中知道它们,除非你明确地将那个cc文件#include到头文件或调用它们的文件中,这是由于解析器工作。

(That's why template function definitions are generally kept in the header files, as Earwicker described.) (这就是为什么模板函数定义通常保存在头文件中,正如Earwicker所描述的那样。)

Any clearer? 有更清楚的吗?

I believe Earwicker is correct. 我相信Earwicker是正确的。 The problem with explicitly instantiating the template member function func in this case is that the type returned by boost::bind is implementation dependent. 在这种情况下显式实例化模板成员函数func的问题是boost :: bind返回的类型是依赖于实现的。 It is not a boost::function. 不是 boost :: function。 A boost::function can contain a boost:bind because it has a template assignment operator that deduces the type of the right-hand side (the boost::bind result). boost :: function可以包含 boost:bind,因为它有一个模板赋值运算符,可以推导出右侧的类型(boost :: bind结果)。 In this particular use of func in caller.cc, with this particular implementation of boost, the type of the boost::bind is actually they type mentioned in the linker error between the < and > (ie. boost::_bi::bind_t... ). 在caller.cc中func的这种特殊用法中,使用boost的这个特定实现,boost :: bind的类型实际上是<和>之间的链接器错误中提到的类型(即boost::_bi::bind_t... But explicitly instantiating func for that type probably will have portability problems. 但是明确地为该类型实例化func可能会有可移植性问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM