如何识别函数模板中的stl容器?

时间:2013-11-23 00:08:11

标签: templates c++11 stl

我要做的是定义一个函数模板,如果typename代表一个容器,它会在元素上执行自我应用,例如:

template<typename T> 
function void apply( T &t ){
    if( is_container<T>::value )
        for( auto &e : t ) apply(e);
    else
        // do something to t
        t = ...;
}

我查看了<type_traits>标题,但我不确定是否有可以使用的内容。

对于我自己的类,我希望能够控制它是否必须像容器或元素一样。

有人知道是否有一个很好的方法来做到这一点?

2 个答案:

答案 0 :(得分:3)

一般情况下,你不能这样做。

问题是,即使参数不是容器,函数模板中的每一行都必须是有效的语法。在您的代码中,当for (auto &e : t)为false时,is_container<T>::value部分可能无法编译,因此整个函数无法编译。

有一些关于“静态if”的建议允许编译器根据编译时常量有条件地忽略整个代码块,但它们存在严重问题(请参阅Static If Considered链接提出和讨论他们的问题)。

解决问题的安全且可移植的方法是根据您要运行的代码块调度到不同的函数:

template<typename T> 
  void apply( T &t );   // forward declaration

template<typename T> 
  void apply2( T &t, std::true_type ){
    for( auto &e : t ) apply(e);
  }

template<typename T> 
  void apply2( T &t, std::false_type ){
    // do something to t
    t = ...;
  }

template<typename T> 
  void apply( T &t ){
    apply2(t, std::integral_constant<bool, is_container<T>::value>() );
  }

这确保只有当T是容器时才能使用的代码只有在T确实是容器时才会被调用。这称为tag dispatching

现在要完成这项工作所需要的只是一个特性is_container,一旦你决定容器的意思,这很容易写: - )

没有标准的“is container”类型特征,但是如果您认为“容器”的定义是具有begin()end()成员的东西,您可以编写一个特征来测试它们:

template<typename T>
struct is_container_helper
{
  template<typename U,
           typename Require1 = decltype(std::declval<const U&>().begin()),
           typename Require2 = decltype(std::declval<const U&>().end())>
    static std::true_type test(const U*);

  template<typename U>
    static std::false_type test(...);

  typedef decltype(test<T>(0)) type;
};

template<typename T>
struct is_container : is_container_helper<T>::type { };

鉴于此特性,您可以将其专门用于您自己的类型。

或者,您可以使用SFINAE代替标记调度,这是使is_container_helper特征在上面工作的技巧。这将不允许您如此轻松地为您自己的类型专门化特征。

答案 1 :(得分:-1)

我认为传统上如果某个类型包含value_type typedef,那么它就是一个容器。

或者,您可以查找iterator typedef,它可能更强大。