你在哪里找到有用的模板?

时间:2022-07-14 23:23:52

At my workplace, we tend to use iostream, string, vector, map, and the odd algorithm or two. We haven't actually found many situations where template techniques were a best solution to a problem.

在我的工作场所,我们倾向于使用iostream,字符串,向量,地图和奇数算法或两个。我们实际上并没有发现模板技术是解决问题的最佳方法。

What I am looking for here are ideas, and optionally sample code that shows how you used a template technique to create a new solution to a problem that you encountered in real life.

我在这里寻找的是想法,以及可选的示例代码,它们展示了您如何使用模板技术为您在现实生活中遇到的问题创建新的解决方案。

As a bribe, expect an up vote for your answer.

作为贿赂,期待对你的答案进行投票。

12 个解决方案

#1


8  

I've used a lot of template code, mostly in Boost and the STL, but I've seldom had a need to write any.

我使用了很多模板代码,主要是在Boost和STL中,但我很少需要编写任何代码。

One of the exceptions, a few years ago, was in a program that manipulated Windows PE-format EXE files. The company wanted to add 64-bit support, but the ExeFile class that I'd written to handle the files only worked with 32-bit ones. The code required to manipulate the 64-bit version was essentially identical, but it needed to use a different address type (64-bit instead of 32-bit), which caused two other data structures to be different as well.

几年前的一个例外是在一个操作Windows PE格式EXE文件的程序中。该公司希望添加64位支持,但我编写的用于处理文件的ExeFile类仅适用于32位版本。操作64位版本所需的代码基本相同,但它需要使用不同的地址类型(64位而不是32位),这导致其他两个数据结构也不同。

Based on the STL's use of a single template to support both std::string and std::wstring, I decided to try making ExeFile a template, with the differing data structures and the address type as parameters. There were two places where I still had to use #ifdef WIN64 lines (slightly different processing requirements), but it wasn't really difficult to do. We've got full 32- and 64-bit support in that program now, and using the template means that every modification we've done since automatically applies to both versions.

基于STL使用单个模板来支持std :: string和std :: wstring,我决定尝试将ExeFile作为模板,使用不同的数据结构和地址类型作为参数。有两个地方我仍然需要使用#ifdef WIN64线路(处理要求略有不同),但这并不是很难。我们现在已经在该程序中获得了完整的32位和64位支持,并且使用该模板意味着我们所做的每一次修改都会自动应用于这两个版本。

#2


11  

General info on templates:

模板的一般信息:

Templates are useful anytime you need to use the same code but operating on different data types, where the types are known at compile time. And also when you have any kind of container object.

当您需要使用相同的代码但在不同的数据类型上运行时,模板非常有用,其中类型在编译时是已知的。当你有任何类型的容器对象时。

A very common usage is for just about every type of data structure. For example: Singly linked lists, doubly linked lists, trees, tries, hashtables, ...

几乎所有类型的数据结构都是一种非常常见的用法。例如:单链表,双链表,树,尝试,哈希表,...

Another very common usage is for sorting algorithms.

另一个非常常见的用法是排序算法。

One of the main advantages of using templates is that you can remove code duplication. Code duplication is one of the biggest things you should avoid when programming.

使用模板的一个主要优点是可以删除代码重复。代码重复是编程时应该避免的最重要的事情之一。

You could implement a function Max as both a macro or a template, but the template implementation would be type safe and therefore better.

您可以将函数Max实现为宏或模板,但模板实现将是类型安全的,因此更好。

And now onto the cool stuff:

而现在到了很酷的东西:

Also see template metaprogramming, which is a way of pre-evaluating code at compile-time rather than at run-time. Template metaprogramming has only immutable variables, and therefore its variables cannot change. Because of this template metaprogramming can be seen as a type of functional programming.

另请参阅模板元编程,这是一种在编译时而不是在运行时预先评估代码的方法。模板元编程只有不可变的变量,因此它的变量不能改变。由于这个模板,元编程可以看作是一种函数式编程。

Check out this example of template metaprogramming from Wikipedia. It shows how templates can be used to execute code at compile time. Therefore at runtime you have a pre-calculated constant.

查看来自*的模板元编程示例。它显示了如何在编译时使用模板来执行代码。因此,在运行时,您有一个预先计算的常量。

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}

#3


7  

One place that I do use templates to create my own code is to implement policy classes as described by Andrei Alexandrescu in Modern C++ Design. At present I'm working on a project that includes a set of classes that interact with BEA\h\h\h Oracle's Tuxedo TP monitor.

我使用模板创建自己的代码的一个地方是实现策略类,如Andrei Alexandrescu在Modern C ++ Design中所描述的。目前我正在开发一个项目,其中包括一组与BEA \ h \ h \ h Oracle的Tuxedo TP监视器交互的类。

One facility that Tuxedo provides is transactional persistant queues, so I have a class TpQueue that interacts with the queue:

Tuxedo提供的一个工具是事务持久性队列,所以我有一个与队列交互的类TpQueue:

class TpQueue {
public:
   void enqueue(...)
   void dequeue(...)
   ...
}

However as the queue is transactional I need to decide what transaction behaviour I want; this could be done seperately outside of the TpQueue class but I think it's more explicit and less error prone if each TpQueue instance has its own policy on transactions. So I have a set of TransactionPolicy classes such as:

但是由于队列是事务性的,我需要决定我想要的事务行为;这可以单独在TpQueue类之外完成,但我认为如果每个TpQueue实例在事务上都有自己的策略,那么它更明确且更不容易出错。所以我有一组TransactionPolicy类,例如:

class OwnTransaction {
public:
   begin(...)  // Suspend any open transaction and start a new one
   commit(..)  // Commit my transaction and resume any suspended one
   abort(...)
}

class SharedTransaction {
public:
   begin(...)  // Join the currently active transaction or start a new one if there isn't one
   ...
}

And the TpQueue class gets re-written as

并且TpQueue类被重写为

template <typename TXNPOLICY = SharedTransaction>
class TpQueue : public TXNPOLICY {
   ...
}

So inside TpQueue I can call begin(), abort(), commit() as needed but can change the behaviour based on the way I declare the instance:

所以在TpQueue中我可以根据需要调用begin(),abort(),commit(),但可以根据我声明实例的方式更改行为:

TpQueue<SharedTransaction> queue1 ;
TpQueue<OwnTransaction> queue2 ;

#4


4  

I used templates (with the help of Boost.Fusion) to achieve type-safe integers for a hypergraph library that I was developing. I have a (hyper)edge ID and a vertex ID both of which are integers. With templates, vertex and hyperedge IDs became different types and using one when the other was expected generated a compile-time error. Saved me a lot of headache that I'd otherwise have with run-time debugging.

我使用模板(在Boost.Fusion的帮助下)来实现我正在开发的超图库的类型安全整数。我有一个(超)边缘ID和一个顶点ID,它们都是整数。对于模板,vertex和hyperedge ID成为不同的类型,当另一个预期时使用一个ID会产生编译时错误。为我节省了很多头痛,否则我会遇到运行时调试。

#5


3  

Here's one example from a real project. I have getter functions like this:

这是一个真实项目的例子。我有这样的getter函数:

bool getValue(wxString key, wxString& value);
bool getValue(wxString key, int& value);
bool getValue(wxString key, double& value);
bool getValue(wxString key, bool& value);
bool getValue(wxString key, StorageGranularity& value);
bool getValue(wxString key, std::vector<wxString>& value);

And then a variant with the 'default' value. It returns the value for key if it exists, or default value if it doesn't. Template saved me from having to create 6 new functions myself.

然后是具有“默认”值的变体。如果key存在则返回key的值,如果不存在则返回默认值。模板使我不必自己创建6个新功能。

template <typename T>
T get(wxString key, const T& defaultValue)
{
    T temp;
    if (getValue(key, temp))
        return temp;
    else
        return defaultValue;
}

#6


2  

Templates I regulary consume are a multitude of container classes, boost smart pointers, scopeguards, a few STL algorithms.

我经常使用的模板是众多容器类,提升智能指针,示波器,一些STL算法。

Scenarios in which I have written templates:

我编写模板的场景:

  • custom containers
  • memory management, implementing type safety and CTor/DTor invocation on top of void * allocators
  • 内存管理,在void * allocators之上实现类型安全和CTor / DTor调用

  • common implementation for overloads wiht different types, e.g.

    具有不同类型的重载的通用实现,例如,

    bool ContainsNan(float * , int) bool ContainsNan(double *, int)

    bool ContainsNan(float *,int)bool ContainsNan(double *,int)

which both just call a (local, hidden) helper function

它们都只调用(本地,隐藏)辅助函数

template <typename T>
bool ContainsNanT<T>(T * values, int len) { ... actual code goes here } ;

Specific algorithms that are independent of the type, as long as the type has certain properties, e.g. binary serialization.

只要该类型具有某些属性,例如,与该类型无关的特定算法。二进制序列化。

template <typename T>
void BinStream::Serialize(T & value) { ... }

// to make a type serializable, you need to implement
void SerializeElement(BinStream & strean, Foo & element);
void DeserializeElement(BinStream & stream, Foo & element)

Unlike virtual functions, templates allow more optimizations to take place.

与虚函数不同,模板允许进行更多优化。


Generally, templates allow to implement one concept or algorithm for a multitude of types, and have the differences resolved already at compile time.

通常,模板允许为多种类型实现一个概念或算法,并且在编译时已经解决了差异。

#7


2  

We use COM and accept a pointer to an object that can either implement another interface directly or via [IServiceProvider](http://msdn.microsoft.com/en-us/library/cc678965(VS.85).aspx) this prompted me to create this helper cast-like function.

我们使用COM并接受指向对象的指针,该对象可以直接实现另一个接口,也可以通过[IServiceProvider](http://msdn.microsoft.com/en-us/library/cc678965(VS.85).aspx)提示我创建这个助手类似于Cast的功能。

// Get interface either via QueryInterface of via QueryService
template <class IFace>
CComPtr<IFace> GetIFace(IUnknown* unk)
{
    CComQIPtr<IFace> ret = unk; // Try QueryInterface
    if (ret == NULL) { // Fallback to QueryService
        if(CComQIPtr<IServiceProvider> ser = unk)
            ser->QueryService(__uuidof(IFace), __uuidof(IFace), (void**)&ret);
    }
    return ret;
}

#8


1  

I use templates to specify function object types. I often write code that takes a function object as an argument -- a function to integrate, a function to optimize, etc. -- and I find templates more convenient than inheritance. So my code receiving a function object -- such as an integrator or optimizer -- has a template parameter to specify the kind of function object it operates on.

我使用模板来指定函数对象类型。我经常编写将函数对象作为参数的代码 - 集成函数,优化函数等等 - 我发现模板比继承更方便。所以我的代码接收一个函数对象 - 比如积分器或优化器 - 有一个模板参数来指定它操作的函数对象的类型。

#9


1  

The obvious reasons (like preventing code-duplication by operating on different data types) aside, there is this really cool pattern that's called policy based design. I have asked a question about policies vs strategies.

显而易见的原因(比如通过操作不同的数据类型来防止代码重复)除此之外,还有一种非常酷的模式,称为基于策略的设计。我问了一个关于政策与策略的问题。

Now, what's so nifty about this feature. Consider you are writing an interface for others to use. You know that your interface will be used, because it is a module in its own domain. But you don't know yet how people are going to use it. Policy-based design strengthens your code for future reuse; it makes you independent of data types a particular implementation relies on. The code is just "slurped in". :-)

现在,这个功能真是太棒了。考虑您正在编写一个供其他人使用的界面。您知道将使用您的界面,因为它是自己域中的模块。但你还不知道人们将如何使用它。基于策略的设计可以增强您的代码,以便将来重用;它使您独立于特定实现所依赖的数据类型。代码只是“悄悄进入”。 :-)

Traits are per se a wonderful idea. They can attach particular behaviour, data and typedata to a model. Traits allow complete parameterization of all of these three fields. And the best of it, it's a very good way to make code reusable.

特质本身就是个好主意。他们可以将特定行为,数据和类型数据附加到模型。特征允许完成所有这三个字段的参数化。最好的,它是使代码可重用的一种非常好的方法。

#10


1  

I once saw the following code:

我曾经看过以下代码:

void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
   // three lines of code
   callFunctionGeneric1(c) ;
   // three lines of code
}

repeated ten times:

重复十次:

void doSomethingGeneric2(SomeClass * c, SomeClass & d)
void doSomethingGeneric3(SomeClass * c, SomeClass & d)
void doSomethingGeneric4(SomeClass * c, SomeClass & d)
// Etc

Each function having the same 6 lines of code copy/pasted, and each time calling another function callFunctionGenericX with the same number suffix.

每个函数具有相同的6行代码复制/粘贴,并且每次调用具有相同数字后缀的另一个函数callFunctionGenericX。

There were no way to refactor the whole thing altogether. So I kept the refactoring local.

没有办法完全重构整个事情。所以我保留了当地的重构。

I changed the code this way (from memory):

我这样改变了代码(从内存中):

template<typename T>
void doSomethingGenericAnything(SomeClass * c, SomeClass & d, T t)
{
   // three lines of code
   t(c) ;
   // three lines of code
}

And modified the existing code with:

并修改现有代码:

void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
   doSomethingGenericAnything(c, d, callFunctionGeneric1) ;
}

void doSomethingGeneric2(SomeClass * c, SomeClass & d)
{
   doSomethingGenericAnything(c, d, callFunctionGeneric2) ;
}

Etc.

This is somewhat highjacking the template thing, but in the end, I guess it's better than play with typedefed function pointers or using macros.

这有点劫持模板的事情,但最后,我想它比使用typedefed函数指针或使用宏更好。

#11


1  

I personally have used the Curiously Recurring Template Pattern as a means of enforcing some form of top-down design and bottom-up implementation. An example would be a specification for a generic handler where certain requirements on both form and interface are enforced on derived types at compile time. It looks something like this:

我个人使用了奇怪的重复模板模式作为强制执行某种形式的自上而下设计和自下而上实现的方法。一个示例是通用处理程序的规范,其中在编译时对派生类型强制执行表单和接口上的某些要求。它看起来像这样:

template <class Derived>
struct handler_base : Derived {
  void pre_call() {
    // do any universal pre_call handling here
    static_cast<Derived *>(this)->pre_call();
  };

  void post_call(typename Derived::result_type & result) {
    static_cast<Derived *>(this)->post_call(result);
    // do any universal post_call handling here
  };

  typename Derived::result_type
  operator() (typename Derived::arg_pack const & args) {
    pre_call();
    typename Derived::result_type temp = static_cast<Derived *>(this)->eval(args);
    post_call(temp);
    return temp;
  };

};

Something like this can be used then to make sure your handlers derive from this template and enforce top-down design and then allow for bottom-up customization:

可以使用这样的东西来确保您的处理程序派生自此模板并强制执行自上而下的设计,然后允许自下而上的自定义:

struct my_handler : handler_base<my_handler> {
  typedef int result_type; // required to compile
  typedef tuple<int, int> arg_pack; // required to compile
  void pre_call(); // required to compile
  void post_call(int &); // required to compile
  int eval(arg_pack const &); // required to compile
};

This then allows you to have generic polymorphic functions that deal with only handler_base<> derived types:

然后,这允许您具有仅处理handler_base <>派生类型的通用多态函数:

template <class T, class Arg0, class Arg1>
typename T::result_type
invoke(handler_base<T> & handler, Arg0 const & arg0, Arg1 const & arg1) {
  return handler(make_tuple(arg0, arg1));
};

#12


1  

It's already been mentioned that you can use templates as policy classes to do something. I use this a lot.

已经提到过,您可以使用模板作为策略类来执行某些操作。我经常使用它。

I also use them, with the help of property maps (see boost site for more information on this), in order to access data in a generic way. This gives the opportunity to change the way you store data, without ever having to change the way you retrieve it.

我还使用它们,在属性映射的帮助下(有关此更多信息,请参阅boost站点),以便以通用方式访问数据。这使您有机会更改存储数据的方式,而无需更改检索数据的方式。

#1


8  

I've used a lot of template code, mostly in Boost and the STL, but I've seldom had a need to write any.

我使用了很多模板代码,主要是在Boost和STL中,但我很少需要编写任何代码。

One of the exceptions, a few years ago, was in a program that manipulated Windows PE-format EXE files. The company wanted to add 64-bit support, but the ExeFile class that I'd written to handle the files only worked with 32-bit ones. The code required to manipulate the 64-bit version was essentially identical, but it needed to use a different address type (64-bit instead of 32-bit), which caused two other data structures to be different as well.

几年前的一个例外是在一个操作Windows PE格式EXE文件的程序中。该公司希望添加64位支持,但我编写的用于处理文件的ExeFile类仅适用于32位版本。操作64位版本所需的代码基本相同,但它需要使用不同的地址类型(64位而不是32位),这导致其他两个数据结构也不同。

Based on the STL's use of a single template to support both std::string and std::wstring, I decided to try making ExeFile a template, with the differing data structures and the address type as parameters. There were two places where I still had to use #ifdef WIN64 lines (slightly different processing requirements), but it wasn't really difficult to do. We've got full 32- and 64-bit support in that program now, and using the template means that every modification we've done since automatically applies to both versions.

基于STL使用单个模板来支持std :: string和std :: wstring,我决定尝试将ExeFile作为模板,使用不同的数据结构和地址类型作为参数。有两个地方我仍然需要使用#ifdef WIN64线路(处理要求略有不同),但这并不是很难。我们现在已经在该程序中获得了完整的32位和64位支持,并且使用该模板意味着我们所做的每一次修改都会自动应用于这两个版本。

#2


11  

General info on templates:

模板的一般信息:

Templates are useful anytime you need to use the same code but operating on different data types, where the types are known at compile time. And also when you have any kind of container object.

当您需要使用相同的代码但在不同的数据类型上运行时,模板非常有用,其中类型在编译时是已知的。当你有任何类型的容器对象时。

A very common usage is for just about every type of data structure. For example: Singly linked lists, doubly linked lists, trees, tries, hashtables, ...

几乎所有类型的数据结构都是一种非常常见的用法。例如:单链表,双链表,树,尝试,哈希表,...

Another very common usage is for sorting algorithms.

另一个非常常见的用法是排序算法。

One of the main advantages of using templates is that you can remove code duplication. Code duplication is one of the biggest things you should avoid when programming.

使用模板的一个主要优点是可以删除代码重复。代码重复是编程时应该避免的最重要的事情之一。

You could implement a function Max as both a macro or a template, but the template implementation would be type safe and therefore better.

您可以将函数Max实现为宏或模板,但模板实现将是类型安全的,因此更好。

And now onto the cool stuff:

而现在到了很酷的东西:

Also see template metaprogramming, which is a way of pre-evaluating code at compile-time rather than at run-time. Template metaprogramming has only immutable variables, and therefore its variables cannot change. Because of this template metaprogramming can be seen as a type of functional programming.

另请参阅模板元编程,这是一种在编译时而不是在运行时预先评估代码的方法。模板元编程只有不可变的变量,因此它的变量不能改变。由于这个模板,元编程可以看作是一种函数式编程。

Check out this example of template metaprogramming from Wikipedia. It shows how templates can be used to execute code at compile time. Therefore at runtime you have a pre-calculated constant.

查看来自*的模板元编程示例。它显示了如何在编译时使用模板来执行代码。因此,在运行时,您有一个预先计算的常量。

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}

#3


7  

One place that I do use templates to create my own code is to implement policy classes as described by Andrei Alexandrescu in Modern C++ Design. At present I'm working on a project that includes a set of classes that interact with BEA\h\h\h Oracle's Tuxedo TP monitor.

我使用模板创建自己的代码的一个地方是实现策略类,如Andrei Alexandrescu在Modern C ++ Design中所描述的。目前我正在开发一个项目,其中包括一组与BEA \ h \ h \ h Oracle的Tuxedo TP监视器交互的类。

One facility that Tuxedo provides is transactional persistant queues, so I have a class TpQueue that interacts with the queue:

Tuxedo提供的一个工具是事务持久性队列,所以我有一个与队列交互的类TpQueue:

class TpQueue {
public:
   void enqueue(...)
   void dequeue(...)
   ...
}

However as the queue is transactional I need to decide what transaction behaviour I want; this could be done seperately outside of the TpQueue class but I think it's more explicit and less error prone if each TpQueue instance has its own policy on transactions. So I have a set of TransactionPolicy classes such as:

但是由于队列是事务性的,我需要决定我想要的事务行为;这可以单独在TpQueue类之外完成,但我认为如果每个TpQueue实例在事务上都有自己的策略,那么它更明确且更不容易出错。所以我有一组TransactionPolicy类,例如:

class OwnTransaction {
public:
   begin(...)  // Suspend any open transaction and start a new one
   commit(..)  // Commit my transaction and resume any suspended one
   abort(...)
}

class SharedTransaction {
public:
   begin(...)  // Join the currently active transaction or start a new one if there isn't one
   ...
}

And the TpQueue class gets re-written as

并且TpQueue类被重写为

template <typename TXNPOLICY = SharedTransaction>
class TpQueue : public TXNPOLICY {
   ...
}

So inside TpQueue I can call begin(), abort(), commit() as needed but can change the behaviour based on the way I declare the instance:

所以在TpQueue中我可以根据需要调用begin(),abort(),commit(),但可以根据我声明实例的方式更改行为:

TpQueue<SharedTransaction> queue1 ;
TpQueue<OwnTransaction> queue2 ;

#4


4  

I used templates (with the help of Boost.Fusion) to achieve type-safe integers for a hypergraph library that I was developing. I have a (hyper)edge ID and a vertex ID both of which are integers. With templates, vertex and hyperedge IDs became different types and using one when the other was expected generated a compile-time error. Saved me a lot of headache that I'd otherwise have with run-time debugging.

我使用模板(在Boost.Fusion的帮助下)来实现我正在开发的超图库的类型安全整数。我有一个(超)边缘ID和一个顶点ID,它们都是整数。对于模板,vertex和hyperedge ID成为不同的类型,当另一个预期时使用一个ID会产生编译时错误。为我节省了很多头痛,否则我会遇到运行时调试。

#5


3  

Here's one example from a real project. I have getter functions like this:

这是一个真实项目的例子。我有这样的getter函数:

bool getValue(wxString key, wxString& value);
bool getValue(wxString key, int& value);
bool getValue(wxString key, double& value);
bool getValue(wxString key, bool& value);
bool getValue(wxString key, StorageGranularity& value);
bool getValue(wxString key, std::vector<wxString>& value);

And then a variant with the 'default' value. It returns the value for key if it exists, or default value if it doesn't. Template saved me from having to create 6 new functions myself.

然后是具有“默认”值的变体。如果key存在则返回key的值,如果不存在则返回默认值。模板使我不必自己创建6个新功能。

template <typename T>
T get(wxString key, const T& defaultValue)
{
    T temp;
    if (getValue(key, temp))
        return temp;
    else
        return defaultValue;
}

#6


2  

Templates I regulary consume are a multitude of container classes, boost smart pointers, scopeguards, a few STL algorithms.

我经常使用的模板是众多容器类,提升智能指针,示波器,一些STL算法。

Scenarios in which I have written templates:

我编写模板的场景:

  • custom containers
  • memory management, implementing type safety and CTor/DTor invocation on top of void * allocators
  • 内存管理,在void * allocators之上实现类型安全和CTor / DTor调用

  • common implementation for overloads wiht different types, e.g.

    具有不同类型的重载的通用实现,例如,

    bool ContainsNan(float * , int) bool ContainsNan(double *, int)

    bool ContainsNan(float *,int)bool ContainsNan(double *,int)

which both just call a (local, hidden) helper function

它们都只调用(本地,隐藏)辅助函数

template <typename T>
bool ContainsNanT<T>(T * values, int len) { ... actual code goes here } ;

Specific algorithms that are independent of the type, as long as the type has certain properties, e.g. binary serialization.

只要该类型具有某些属性,例如,与该类型无关的特定算法。二进制序列化。

template <typename T>
void BinStream::Serialize(T & value) { ... }

// to make a type serializable, you need to implement
void SerializeElement(BinStream & strean, Foo & element);
void DeserializeElement(BinStream & stream, Foo & element)

Unlike virtual functions, templates allow more optimizations to take place.

与虚函数不同,模板允许进行更多优化。


Generally, templates allow to implement one concept or algorithm for a multitude of types, and have the differences resolved already at compile time.

通常,模板允许为多种类型实现一个概念或算法,并且在编译时已经解决了差异。

#7


2  

We use COM and accept a pointer to an object that can either implement another interface directly or via [IServiceProvider](http://msdn.microsoft.com/en-us/library/cc678965(VS.85).aspx) this prompted me to create this helper cast-like function.

我们使用COM并接受指向对象的指针,该对象可以直接实现另一个接口,也可以通过[IServiceProvider](http://msdn.microsoft.com/en-us/library/cc678965(VS.85).aspx)提示我创建这个助手类似于Cast的功能。

// Get interface either via QueryInterface of via QueryService
template <class IFace>
CComPtr<IFace> GetIFace(IUnknown* unk)
{
    CComQIPtr<IFace> ret = unk; // Try QueryInterface
    if (ret == NULL) { // Fallback to QueryService
        if(CComQIPtr<IServiceProvider> ser = unk)
            ser->QueryService(__uuidof(IFace), __uuidof(IFace), (void**)&ret);
    }
    return ret;
}

#8


1  

I use templates to specify function object types. I often write code that takes a function object as an argument -- a function to integrate, a function to optimize, etc. -- and I find templates more convenient than inheritance. So my code receiving a function object -- such as an integrator or optimizer -- has a template parameter to specify the kind of function object it operates on.

我使用模板来指定函数对象类型。我经常编写将函数对象作为参数的代码 - 集成函数,优化函数等等 - 我发现模板比继承更方便。所以我的代码接收一个函数对象 - 比如积分器或优化器 - 有一个模板参数来指定它操作的函数对象的类型。

#9


1  

The obvious reasons (like preventing code-duplication by operating on different data types) aside, there is this really cool pattern that's called policy based design. I have asked a question about policies vs strategies.

显而易见的原因(比如通过操作不同的数据类型来防止代码重复)除此之外,还有一种非常酷的模式,称为基于策略的设计。我问了一个关于政策与策略的问题。

Now, what's so nifty about this feature. Consider you are writing an interface for others to use. You know that your interface will be used, because it is a module in its own domain. But you don't know yet how people are going to use it. Policy-based design strengthens your code for future reuse; it makes you independent of data types a particular implementation relies on. The code is just "slurped in". :-)

现在,这个功能真是太棒了。考虑您正在编写一个供其他人使用的界面。您知道将使用您的界面,因为它是自己域中的模块。但你还不知道人们将如何使用它。基于策略的设计可以增强您的代码,以便将来重用;它使您独立于特定实现所依赖的数据类型。代码只是“悄悄进入”。 :-)

Traits are per se a wonderful idea. They can attach particular behaviour, data and typedata to a model. Traits allow complete parameterization of all of these three fields. And the best of it, it's a very good way to make code reusable.

特质本身就是个好主意。他们可以将特定行为,数据和类型数据附加到模型。特征允许完成所有这三个字段的参数化。最好的,它是使代码可重用的一种非常好的方法。

#10


1  

I once saw the following code:

我曾经看过以下代码:

void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
   // three lines of code
   callFunctionGeneric1(c) ;
   // three lines of code
}

repeated ten times:

重复十次:

void doSomethingGeneric2(SomeClass * c, SomeClass & d)
void doSomethingGeneric3(SomeClass * c, SomeClass & d)
void doSomethingGeneric4(SomeClass * c, SomeClass & d)
// Etc

Each function having the same 6 lines of code copy/pasted, and each time calling another function callFunctionGenericX with the same number suffix.

每个函数具有相同的6行代码复制/粘贴,并且每次调用具有相同数字后缀的另一个函数callFunctionGenericX。

There were no way to refactor the whole thing altogether. So I kept the refactoring local.

没有办法完全重构整个事情。所以我保留了当地的重构。

I changed the code this way (from memory):

我这样改变了代码(从内存中):

template<typename T>
void doSomethingGenericAnything(SomeClass * c, SomeClass & d, T t)
{
   // three lines of code
   t(c) ;
   // three lines of code
}

And modified the existing code with:

并修改现有代码:

void doSomethingGeneric1(SomeClass * c, SomeClass & d)
{
   doSomethingGenericAnything(c, d, callFunctionGeneric1) ;
}

void doSomethingGeneric2(SomeClass * c, SomeClass & d)
{
   doSomethingGenericAnything(c, d, callFunctionGeneric2) ;
}

Etc.

This is somewhat highjacking the template thing, but in the end, I guess it's better than play with typedefed function pointers or using macros.

这有点劫持模板的事情,但最后,我想它比使用typedefed函数指针或使用宏更好。

#11


1  

I personally have used the Curiously Recurring Template Pattern as a means of enforcing some form of top-down design and bottom-up implementation. An example would be a specification for a generic handler where certain requirements on both form and interface are enforced on derived types at compile time. It looks something like this:

我个人使用了奇怪的重复模板模式作为强制执行某种形式的自上而下设计和自下而上实现的方法。一个示例是通用处理程序的规范,其中在编译时对派生类型强制执行表单和接口上的某些要求。它看起来像这样:

template <class Derived>
struct handler_base : Derived {
  void pre_call() {
    // do any universal pre_call handling here
    static_cast<Derived *>(this)->pre_call();
  };

  void post_call(typename Derived::result_type & result) {
    static_cast<Derived *>(this)->post_call(result);
    // do any universal post_call handling here
  };

  typename Derived::result_type
  operator() (typename Derived::arg_pack const & args) {
    pre_call();
    typename Derived::result_type temp = static_cast<Derived *>(this)->eval(args);
    post_call(temp);
    return temp;
  };

};

Something like this can be used then to make sure your handlers derive from this template and enforce top-down design and then allow for bottom-up customization:

可以使用这样的东西来确保您的处理程序派生自此模板并强制执行自上而下的设计,然后允许自下而上的自定义:

struct my_handler : handler_base<my_handler> {
  typedef int result_type; // required to compile
  typedef tuple<int, int> arg_pack; // required to compile
  void pre_call(); // required to compile
  void post_call(int &); // required to compile
  int eval(arg_pack const &); // required to compile
};

This then allows you to have generic polymorphic functions that deal with only handler_base<> derived types:

然后,这允许您具有仅处理handler_base <>派生类型的通用多态函数:

template <class T, class Arg0, class Arg1>
typename T::result_type
invoke(handler_base<T> & handler, Arg0 const & arg0, Arg1 const & arg1) {
  return handler(make_tuple(arg0, arg1));
};

#12


1  

It's already been mentioned that you can use templates as policy classes to do something. I use this a lot.

已经提到过,您可以使用模板作为策略类来执行某些操作。我经常使用它。

I also use them, with the help of property maps (see boost site for more information on this), in order to access data in a generic way. This gives the opportunity to change the way you store data, without ever having to change the way you retrieve it.

我还使用它们,在属性映射的帮助下(有关此更多信息,请参阅boost站点),以便以通用方式访问数据。这使您有机会更改存储数据的方式,而无需更改检索数据的方式。