8

我有两个半密切相关的问题。给定作为模板参数传递的 STL 迭代器类型:

  1. 如何确定类型对应于 const 还是非 const 迭代器?
  2. 作为 1. 的替代方案,如何强制(enable_if例如使用 s)这种类型对应于非常量迭代器?
  3. 如何从非常量中获取迭代器的常量版本(反之亦然)?[注:在本帖中回答;毫不奇怪,你不能。]

这个问题来自哪里:

我写了一个小类来促进对向量的算术/关系/代数运算(向量是指一维固定大小的数据,而不是 STL 向量)。我没有强加一个特定的数据容器,而是定义了一个接口并派生了几个可能的容器,这些容器基本上“包装”了各种存储数据的方式。其中一个容器是 STL 迭代器的包装器,我遇到了一些麻烦。

4

3 回答 3

6

问题一:

您可以使用以下类型特征:

template<typename T, typename = void>
struct is_const_iterator : std::false_type { };

template<typename T>
struct is_const_iterator<T,
    typename std::enable_if<
        std::is_const<
            typename std::remove_pointer<
                typename std::iterator_traits<T>::pointer
                >::type
            >::value
        >::type> : std::true_type { };

这是一个演示:

#include <type_traits>
#include <iterator>
#include <list>
#include <vector>

template<typename T, typename = void>
struct is_const_iterator : std::false_type { };

template<typename T>
struct is_const_iterator<T,
    typename std::enable_if<
        std::is_const<
            typename std::remove_pointer<
                typename std::iterator_traits<T>::pointer
                >::type
            >::value
        >::type> : std::true_type { };

int main()
{
    typedef std::list<int>::iterator LI;
    typedef std::list<int>::const_iterator CLI;
    static_assert(is_const_iterator<LI>::value, "!"); // Fires
    static_assert(is_const_iterator<CLI>::value, "!"); // Does not fire

    typedef std::vector<int>::iterator VI;
    typedef std::vector<int>::const_iterator CVI;
    static_assert(is_const_iterator<VI>::value, "!"); // Fires
    static_assert(is_const_iterator<CVI>::value, "!"); // Does not fire
}

这是一个活生生的例子

问题2:

使用上述类型特征,这变得很简单。假设您有一个foo()要约束的函数模板,以便它只接受非const迭代器:

template<typename It,
    typename std::enable_if<!is_const_iterator<It>::value>::type* = nullptr>
void foo(It i)
{
    // Does something with i...
}

还有一个简单的演示程序:

int main()
{
    std::vector<int> v;
    foo(v.begin()); // OK
    foo(v.cbegin()); // ERROR!
}

这是一个活生生的例子

于 2013-05-11T15:31:44.337 回答
3

对于 1),您可以执行以下操作:

std::is_const<
  typename std::remove_reference<
    typename std::iterator_traits<Iterator>::reference
  >::type
>::value

或这个:

std::is_const<
  typename std::remove_reference<
    decltype(*iterator)
  >::type
>::value

您可以使用这些谓词传递给以std::enable_if实现 2)。

注意:正如 R. Martinho Fernandes 在评论中所指出的,如果所讨论的迭代器对其reference特征使用与普通引用不同的类型(例如std::vector<bool>::const_iterator确实如此),则这些谓词将失败。

于 2013-05-11T15:27:55.553 回答
2

你可以使用 SFINAE

decltype( **(T*)0 = std::move(**(T*)0) )

或(Xeo 的偏好)

decltype( *declval<T&>() = std::move(*declval<T&>()) )

它检查取消引用迭代器是否会给你一些可分配的东西。不完美,如果集合的元素类型是不可分配的,那么const_iterator无论如何有一个 non- 会​​有什么好处?

不要测试const_iterator,测试你的算法实际需要的操作。

于 2013-05-11T15:44:14.133 回答