【问题标题】:How can static_assert be used to check element type of iterator argument to templated function?static_assert 如何用于检查模板化函数的迭代器参数的元素类型?
【发布时间】:2014-11-05 21:42:21
【问题描述】:

我有一个接受输出迭代器参数的函数模板。如何使用static_assert 检查实例化是否使用了适当的迭代器? (即,它既是一个输出迭代器,又是它分配正确类型的元素。)

#include <iostream>
#include <list>
#include <set>

template <class OutputIter>
void add_ints ( OutputIter iter )
{
    static_assert ( something_goes_here,
                    "Arg must be an output iterator over ints" );

    *iter++ = 1;
    *iter++ = 2;
    *iter++ = 3;
}

main()
{
    // Insert iterator will add three elements.
    std::set<int> my_set;
    add_ints ( std::inserter ( my_set, my_set.end() ) );
    for ( int i : my_set ) std::cout << i << "\n";

    // Non-insert iterator will overwrite three elements.
    std::list<int> my_list ( { 0, 0, 0 } );
    add_ints ( my_list.begin() ) );
    for ( int i : my_list ) std::cout << i << "\n";

#if 0
    // Want nice compile error that container elements are not ints.
    std::set<std::string> bad_set;
    add_ints ( std::inserter ( bad_set, bad_set.end() ) );
#endif
#if 0
    // Want nice compile error that argument is not an iterator.
    class Foo {} foo;
    add_ints ( foo );
#endif
}

【问题讨论】:

  • 使用iterator_traits&lt;T&gt;::value_type。您应该使用iterator_traits 进行当前检查以适应没有嵌套iterator_category 类型的类型(不确定这对于OutputIterator 是否可行)。我还认为您应该使用is_base_of 而不是is_same 来测试迭代器类别。如果有人向您传递了一个仍然满足 OutputIterator 的所有要求的更精细的迭代器类别,您的代码应该仍然可以工作(尽管目前标准库中不存在这种类型)。
  • OutputIterators 需要仅支持*r = o,而不是r = o。对于某些标准库输出迭代器,取消引用操作是无操作的这一事实仅是这些迭代器的实现细节,您不应该在通用代码中依赖它。检查std::output_iterator_tag 通常也不是一个好主意。许多可变迭代器会有不同的标签。
  • @Praetorian 这不是你想要的。对于许多OutputIterators,iterator_traits&lt;T&gt;::value_typevoid
  • 您无法可靠地检测输出迭代器。您可以做的最好的事情是 (1) 断言该参数是某种迭代器(std::iterator_traits&lt;T&gt;::iterator_category 存在),并且 (2) 断言可以通过传递给您的任何内容来分配一个整数。
  • static_assert(std::is_assignable&lt;decltype(*iter),int&gt;(), "Jabberwocky is killing user.") 之类的就可以了。

标签: c++ templates iterator typetraits static-assert


【解决方案1】:

OutputIterators 不需要有值类型;他们的value_type 很可能是void,实际上对于标准库的纯输出迭代器来说是void

在您最初的问题中,您检查了output_iterator_tag,但您不应该这样做。有很多完全可变的迭代器具有不同的类别。例如std::vector&lt;int&gt;::iterator 的类别是random_access_iterator_tag

相反,直接检查适用表达式的格式良好。所有Iterators 必须支持*r++r,另外OutputIterators 必须支持*r = or++*r++ = o,所以:

template<class...>
struct make_void { using type = void; };

template<class... Ts>
using void_t = typename make_void<Ts...>::type;

template<class Iter, class U, class = void>
struct is_output_iterator_for : std::false_type {};

template<class Iter, class U>
struct is_output_iterator_for<
    Iter, U, 
    void_t<decltype(++std::declval<Iter&>()),
           decltype(*std::declval<Iter&>() = std::declval<U>()),
           decltype(*std::declval<Iter&>()++ = std::declval<U>())>> : std::true_type {};

【讨论】:

  • @T.C.如果我理解,这是对我原始示例的改进。那么这就是我示例中的static_assert 应该被替换的方式吗? static_assert(is_output_iterator_for&lt;OutputIter, OutputIter&gt;::value, "msg");
  • @JohnH。我相信会是static_assert(is_output_iterator_for&lt;OutputIter, typename OutputIter::value_type&gt;::value, "msg");
  • @0x49 重点是输出迭代器没有值类型。 static_assert(is_output_iterator_for&lt;OutputIter, int&gt;::value, "msg");
  • @T.C.我不假思索地冲了进来,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2017-04-11
  • 2018-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-18
相关资源
最近更新 更多