如何断言模板参数的类型STL迭代器类型

时间:2017-12-31 14:17:21

标签: c++ templates

让我们说我想写一个通用函数来打印出一个集合的标准输出范围。因为它应该是通用的我假设......

std::vector<std::string> names = { "John", "Henry", "Mark" };

以及:

std::vector<int> years = { 100, 200, 400 };

..将有可能打印出来。

由于集合的类型可能不同,并且没有基类用于STL集合,因此我有机会传递基类迭代器,我使用模板函数:

template<typename TIterator>
void PrintRange( TIterator beginIter,TIterator endIter )
{           
    for( auto it = beginIter; it != endIter; ++it )
    {
        std::cout << *it << std::endl;
    }
}

现在一切都很好,现在我可以写:

PrintRange( names.begin(), names.end() );

PrintRange( years.begin(), years.end() );

但是现在我想帮助我的函数客户端更快地理解为什么在使用它时会出现错误。现在我打电话的时候:

PrintRange( 100, 400 );

有错误:

  

main.cpp:23:34:错误:一元'*'的无效类型参数(有'int')

我想打印类似的东西:

  

其中一个参数与预期的类型参数不对应   &#39;迭代&#39;

那么解决这个问题的方法最好:

  1. 关注错误消息并非如此并不重要 像我预期的那样有意义用户应该分析模板类代码 确定他的错误原因。

  2. 使用static_assert断言所有已知的可能性..但是如何断言函数的参数是任何迭代器,因为没有基类?

  3.   

    static_assert(std :: is_base_of :: iterator&gt; :: value);

    这只会断言字符串迭代器的向量...

2 个答案:

答案 0 :(得分:2)

就个人而言,我认为您的第一种方法完全没问题,因此您可能不太关心其他错误消息。

另一方面,如果您决定打印有意义的消息,则可以实现自定义类型特征以检测迭代器,因为它已解释为here,然后将其与static_assert一起使用。所以代码转换成类似的东西:

template<typename TIterator>
void PrintRange(TIterator beginIter, TIterator endIter)
{        
    static_assert(is_iterator<TIterator>::value,
        "TIterator is not an iterator type");

    for( auto it = beginIter; it != endIter; ++it )
    {
        std::cout << *it << std::endl;
    }
}

答案 1 :(得分:0)

Edgar Rokyan提供的答案非常有用,但我知道另一个解决方案(可能更糟糕的是,因为我们必须实现更多的代码)。

这个解决方案不是迭代器的类型检查,而是一个我们可以去哪个方向的提示。根据您的PrintRange函数,我们假设我们需要为TIterator - operator*operator++operator !=定义3个运算符。

要检查是否定义了运算符,可以使用:

template<typename T>
struct has_deref_op{
    private:
        template<typename U>
        static constexpr auto test(int) -> decltype(std::declval<U>().operator*() == 1,
                                                    std::true_type());

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

    public:
        static constexpr bool value = std::is_same<decltype(test<T>(0)),
                                                   std::true_type>::value;
}; 

此代码将检查operator*实施中是否存在T。然后,您可以添加一个static_assert来使用它来验证参数:

template<typename TIterator>
void PrintRange( TIterator beginIter, TIterator endIter )
{
    static_assert(has_deref_op<TIterator>::value, "argument must implement operator*");
    for( auto it = beginIter; it != endIter; ++it )
    {
        std::cout << *it << std::endl;
    }
}

这个解决方案有一个重大缺陷 - 它需要编写大量代码才能简化错误消息。说实话,虽然这种方法可以很好地工作,但我会坚持使用默认的错误消息。这是非常不言自明的 - 如果您提供的int未定义operator*,则会收到相关错误。

编辑:在阅读了Edgar在他的回答中链接的问题之后,它似乎建议实施与此方法类似的is_iterator。我第一次不仔细阅读是不好的