如何“模式匹配”一个模板?

发布于 2024-10-26 16:42:38 字数 299 浏览 1 评论 0 原文

通常在模板中,您想要了解整个类型,但在我的情况下,我需要了解更多,并且想要“分解”类型。举个例子:

template <typename Collection<typename T> >
T get_front(Collection const& c)
{
  return c.front();
}

我怎样才能实现这一目标?注意:我需要它自动推导类型,而不是传递类似 , int>

Normally in templates you want to know the entire type, but in my case I need to know more, and want to "break up" the type. Take this example:

template <typename Collection<typename T> >
T get_front(Collection const& c)
{
  return c.front();
}

How can I achieve that? Note: I need it to to automatically deduce the types, not pass in something like <std::vector<int>, int>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

完美的未来在梦里 2024-11-02 16:42:38

编辑:C++0x的方法可以在最后找到。
编辑2:我很愚蠢,并且比所有这些特征内容更短的方式 C++98/03 方式可以在答案的末尾找到。

如果您希望您的函数适用于任何任意标准库容器,您需要拿出一些模板枪。


问题是,不同的容器采用不同数量的模板参数。例如,std::vectorstd::dequestd::list 取 2:基础项类型 T 和分配器类型 Alloc。另一方面,std::setstd::map 分别采用 3 和 4:两者都有键类型 K,map 采用另一个值类型 V,然后两者都采用比较器 Compare 类型和分配器类型 Alloc。您可以在此处获取标准库提供的所有容器类型的概述。


现在,介绍模板枪。我们将使用部分专门化的特征元结构来获取底层项目类型。 (出于纯粹的偏好,我使用class而不是typename。)

template<class T>
struct ContainerTraits;

// vector, deque, list and even stack and queue (2 template parameters)
template<
    template<class, class> class Container,
    class T, class Other
>
struct ContainerTraits< Container<T,Other> >{
    typedef T value_type;
};

// for set, multiset, and priority_queue (3 template parameters)
template<
    template<class, class, class> class Container,
    class T, class Other1, class Other2
>
struct ContainerTraits< Container<T,Other1,Other2> >{
    typedef T value_type;
};

// for map and multimap (4 template parameters)
template<
    template<class, class, class, class> class Container,
    class Key, class T, class Other1, class Other2
>
struct ContainerTraits< Container<Key,T,Other1,Other2> >{
    typedef Container<Key,T,Other1,Other2> ContainerT;
    // and the map returns pair<const Key,T> from the begin() function
    typedef typename ContainerT::value_type value_type;
};

现在准备工作已经完成,接下来是get_front函数!

template<class Container>
typename ContainerTraits<Container>::value_type
get_front(Container const& c){
    // begin() is the only shared access function
    // to the first element for all standard container (except std::bitset)
    return *c.begin(); 
}

唷!就是这样!完整的示例可以在 Ideone 上查看。当然,可以进一步细化,直到返回映射到 std::map 中的键的实际值,或者使用容器特定的访问函数,但我是只是有点懒得这么做。 :P


编辑
一种更简单的 C++0x 方法是使用新的尾随返回类型函数语法,可以在 在 Ideone 上


编辑2
好吧,我不知道为什么,但在编写这个答案时我完全没有想到嵌​​套的 typedef 。我将保留详细的方式作为匹配模板的特征类/模式的参考。 就是这样做的方法,它基本上与我对特征类所做的相同,但最终不那么冗长。

Edit: A C++0x way can be found at the end.
Edit 2: I'm stupid, and a way shorter C++98/03 way than all this traits stuff can be found at the end of the answer.

If you want your function to work for any arbitary standard library container, you need to pull out some Template Guns.


The thing is, that the different container take a different amount of template parameters. std::vector, std::deque and std::list for example take 2: the underlying item type T and the allocator type Alloc. std::set and std::map on the other hand take 3 and 4 respectively: both have the key type K, map takes another value type V, then both take a comparator Compare type and the allocator type Alloc. You can get an overview of all container types supplied by the standard library for example here.


Now, for the Template Guns. We will be using a partially specialized traits metastruct to get the underlying item type. (I use class instead of typename out of pure preference.)

template<class T>
struct ContainerTraits;

// vector, deque, list and even stack and queue (2 template parameters)
template<
    template<class, class> class Container,
    class T, class Other
>
struct ContainerTraits< Container<T,Other> >{
    typedef T value_type;
};

// for set, multiset, and priority_queue (3 template parameters)
template<
    template<class, class, class> class Container,
    class T, class Other1, class Other2
>
struct ContainerTraits< Container<T,Other1,Other2> >{
    typedef T value_type;
};

// for map and multimap (4 template parameters)
template<
    template<class, class, class, class> class Container,
    class Key, class T, class Other1, class Other2
>
struct ContainerTraits< Container<Key,T,Other1,Other2> >{
    typedef Container<Key,T,Other1,Other2> ContainerT;
    // and the map returns pair<const Key,T> from the begin() function
    typedef typename ContainerT::value_type value_type;
};

Now that the preparation is done, on to the get_front function!

template<class Container>
typename ContainerTraits<Container>::value_type
get_front(Container const& c){
    // begin() is the only shared access function
    // to the first element for all standard container (except std::bitset)
    return *c.begin(); 
}

Phew! And that's it! A full example can be seen on Ideone. Of course it would be possible to refine that even further, to the point of returning the actual value that is mapped to a key in a std::map, or use container specific access functions, but I was just a bit too lazy to do that. :P


Edit
A way easier C++0x way is using the new trailing-return-type function syntax, of which an example can be found here on Ideone.


Edit 2
Well, I don't know why, but I totally didn't think of the nested typedefs when writing this answer. I'll let the verbose way stay as a reference for traits classes / pattern matching a template. This is the way to do it, it is basically the same I did with the traits classes, but ultimately less verbose.

策马西风 2024-11-02 16:42:38

如果您知道它不是关联容器,则可以这样做。

template <typename Collection>
Collection::type_name get_front(Collection const& c)
{
  return c.front();
}

You could do this if you know it's not an associative container.

template <typename Collection>
Collection::type_name get_front(Collection const& c)
{
  return c.front();
}
木有鱼丸 2024-11-02 16:42:38

我假设您想要 CollectionT 作为模板参数。为此,只需输入

template< template < typename > class Collection, typename T >
T get_front( Collection< T > const& c )
...

构造 template <类型名称> class Collection 告诉编译器 Collection 本身就是一个带有一个参数的模板。

编辑
正如 Xeo 所指出的,vector 采用两个模板参数,并且您的模板需要反映这一点, IE

template< template < typename, typename > class Collection, 
          typename T, typename Alloc >
T get_front( Collection< T, Alloc > const& c )
...

I'm assuming you want both Collection and T as template parameters. To do that simply type

template< template < typename > class Collection, typename T >
T get_front( Collection< T > const& c )
...

The construct template < typename > class Collection tells the compiler that Collection is a template itself with one parameter.

Edit:
As pointed out be Xeo, vector takes two template parameters, and your templates need to reflect that, i.e.

template< template < typename, typename > class Collection, 
          typename T, typename Alloc >
T get_front( Collection< T, Alloc > const& c )
...
岁吢 2024-11-02 16:42:38

鉴于 Collection 是提前知道的,我认为你想要的是:

template <typename T>
T get_front(Collection<T> const& c)
{
    return c.front();
}

唯一改变的部分是 T 是什么,它总是在 中集合(内容,而不是容器),因此您不必将其作为模板的一部分。

如果容器发生变化,使用 c.front() 可能会很危险。您需要验证集合类型是否具有不带参数并返回 T 的方法 front

编辑:

如果您确实需要模板Collection,那么这更像是:

template<typename C, typename T>
T get_front(C<T> const & c)

如果可以的话,我会避免通用的东西,也许专门为您知道将使用的集合的功能,或特定类别的类(如果可能)。

Seeing as Collection<T> is known ahead of time, I think what you want is:

template <typename T>
T get_front(Collection<T> const& c)
{
    return c.front();
}

The only part that's changing is what T is, it's always in a Collection (contents, not the container) so you don't have put that as part of the template.

If the container was changing, using c.front() could be dangerous. You would need to verify that the collection type had a method front that took no parameters and return a T.

Edit:

If you do need to template Collection, then that's more like:

template<typename C, typename T>
T get_front(C<T> const & c)

I would avoid something that generic if you can, perhaps specializing the function for collections you know will be used, or to a particular class of classes (if possible).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文