我已经看到一些c++使用模板模板参数(即将模板作为参数的模板)来进行基于策略的类设计的示例。这种技术还有什么其他用途?


当前回答

这是我用过的东西推广出来的。我发布它是因为它是一个非常简单的例子,它演示了一个实际的用例以及默认参数:

#include <vector>

template <class T> class Alloc final { /*...*/ };

template <template <class T> class allocator=Alloc> class MyClass final {
  public:
    std::vector<short,allocator<short>> field0;
    std::vector<float,allocator<float>> field1;
};

其他回答

在使用pfalcon提供的可变参数模板的解决方案中,由于可变参数专门化的贪婪性质,我发现很难实际专门化std::map的ostream操作符。下面是一个对我有用的小修改:

#include <iostream>
#include <vector>
#include <deque>
#include <list>
#include <map>

namespace containerdisplay
{
  template<typename T, template<class,class...> class C, class... Args>
  std::ostream& operator <<(std::ostream& os, const C<T,Args...>& objs)
  {
    std::cout << __PRETTY_FUNCTION__ << '\n';
    for (auto const& obj : objs)
      os << obj << ' ';
    return os;
  }  
}

template< typename K, typename V>
std::ostream& operator << ( std::ostream& os, 
                const std::map< K, V > & objs )
{  

  std::cout << __PRETTY_FUNCTION__ << '\n';
  for( auto& obj : objs )
  {    
    os << obj.first << ": " << obj.second << std::endl;
  }

  return os;
}


int main()
{

  {
    using namespace containerdisplay;
    std::vector<float> vf { 1.1, 2.2, 3.3, 4.4 };
    std::cout << vf << '\n';

    std::list<char> lc { 'a', 'b', 'c', 'd' };
    std::cout << lc << '\n';

    std::deque<int> di { 1, 2, 3, 4 };
    std::cout << di << '\n';
  }

  std::map< std::string, std::string > m1 
  {
      { "foo", "bar" },
      { "baz", "boo" }
  };

  std::cout << m1 << std::endl;

    return 0;
}

我认为你需要使用模板模板语法来传递一个参数,它的类型是依赖于另一个模板的模板,就像这样:

template <template<class> class H, class S>
void f(const H<S> &value) {
}

这里H是一个模板,但我想让这个函数处理H的所有特化。

注意:我已经编程c++很多年了,只需要一次。我发现这是一个很少需要的功能(当然,当你需要它的时候,它很方便!)

我一直在努力想一些好的例子,老实说,大多数时候这是不必要的,但让我们想出一个例子。让我们假设std::vector没有typedef value_type类型。

那么如何编写一个函数来为向量元素创建正确类型的变量呢?这是可行的。

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
    // This can be "typename V<T, A>::value_type",
    // but we are pretending we don't have it

    T temp = v.back();
    v.pop_back();
    // Do some work on temp

    std::cout << temp << std::endl;
}

注意:std::vector有两个模板形参,type和allocator,所以我们必须同时接受它们。幸运的是,由于类型演绎,我们不需要显式地写出确切的类型。

你可以这样用:

f<std::vector, int>(v); // v is of type std::vector<int> using any allocator

或者更好的是,我们可以用:

f(v); // everything is deduced, f can deal with a vector of any type!

更新:即使是这个人为的例子,虽然是说明性的,但由于c++11引入了auto,它不再是一个令人惊讶的例子。现在同样的函数可以写成:

template <class Cont>
void f(Cont &v) {

    auto temp = v.back();
    v.pop_back();
    // Do some work on temp

    std::cout << temp << std::endl;
}

这就是我喜欢写这类代码的方式。

我将它用于版本控制类型。

如果你有一个通过模板控制的类型,比如MyType<version>,你可以写一个函数来捕获版本号:

template<template<uint8_t> T, uint8_t Version>
Foo(const T<Version>& obj)
{
    assert(Version > 2 && "Versions older than 2 are no longer handled");
    ...
    switch (Version)
    {
    ...
    }
}

所以你可以根据传入类型的版本做不同的事情,而不是为每个类型重载。 您还可以使用转换函数,以通用的方式接受MyType<Version>并返回MyType<Version+1>,甚至递归它们以具有ToNewest()函数,该函数从任何旧版本中返回类型的最新版本(对于可能已经存储了一段时间但需要使用今天的最新工具进行处理的日志非常有用)。

这是我遇到的情况:

template<class A>
class B
{
  A& a;
};

template<class B>
class A
{
  B b;
};

class AInstance : A<B<A<B<A<B<A<B<... (oh oh)>>>>>>>>
{

};

可解为:

template<class A>
class B
{
  A& a;
};

template< template<class> class B>
class A
{
  B<A> b;
};

class AInstance : A<B> //happy
{

};

或者(工作代码):

template<class A>
class B
{
public:
    A* a;
    int GetInt() { return a->dummy; }
};

template< template<class> class B>
class A
{
public:
    A() : dummy(3) { b.a = this; }
    B<A> b;
    int dummy;
};

class AInstance : public A<B> //happy
{
public:
    void Print() { std::cout << b.GetInt(); }
};

int main()
{
    std::cout << "hello";
    AInstance test;
    test.Print();
}

这是我用过的东西推广出来的。我发布它是因为它是一个非常简单的例子,它演示了一个实际的用例以及默认参数:

#include <vector>

template <class T> class Alloc final { /*...*/ };

template <template <class T> class allocator=Alloc> class MyClass final {
  public:
    std::vector<short,allocator<short>> field0;
    std::vector<float,allocator<float>> field1;
};