简体   繁体   English

基于以下范围的自定义迭代器:常量问题

[英]Custom iterator in range based for: issue with constness

The following code is based on that found in Modern C++ programming cookbook , and is compiled in VS 2017: 以下代码基于Modern C ++编程指南中的代码 ,并在VS 2017中进行编译:

#include <iostream>

using namespace std;

template <typename T, size_t const Size> 
class dummy_array 
{ 
    T data[Size] = {}; 

public: 
    T const & GetAt(size_t const index) const 
    { 
        if (index < Size) return data[index]; 
        throw std::out_of_range("index out of range"); 
    } 

    // I have added this
    T & GetAt(size_t const index) 
    { 
        if (index < Size) return data[index]; 
        throw std::out_of_range("index out of range"); 
    } 

    void SetAt(size_t const index, T const & value) 
    { 
        if (index < Size) data[index] = value; 
        else throw std::out_of_range("index out of range"); 
    } 

    size_t GetSize() const { return Size; } 
};

template <typename T, typename C, size_t const Size> 
class dummy_array_iterator_type 
{ 
public: 
    dummy_array_iterator_type(C& collection,  
        size_t const index) : 
        index(index), collection(collection) 
    { } 

    bool operator!= (dummy_array_iterator_type const & other) const 
    { 
        return index != other.index; 
    } 

    T const & operator* () const 
    { 
        return collection.GetAt(index); 
    }

    // I have added this
    T & operator* () 
    { 
        return collection.GetAt(index); 
    } 

    dummy_array_iterator_type const & operator++ () 
    { 
        ++index; 
        return *this; 
    } 

private: 
    size_t   index; 
    C&       collection; 
};

template <typename T, size_t const Size> 
using dummy_array_iterator =  dummy_array_iterator_type<T, dummy_array<T, Size>, Size>; 

// I have added the const in 'const dummy_array_iterator_type'
template <typename T, size_t const Size> 
using dummy_array_const_iterator =  const dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;

template <typename T, size_t const Size> 
inline dummy_array_iterator<T, Size> begin(dummy_array<T, Size>& collection) 
{ 
    return dummy_array_iterator<T, Size>(collection, 0); 
} 

template <typename T, size_t const Size> 
inline dummy_array_iterator<T, Size> end(dummy_array<T, Size>& collection) 
{ 
    return dummy_array_iterator<T, Size>(collection, collection.GetSize()); 
} 

template <typename T, size_t const Size> 
inline dummy_array_const_iterator<T, Size> begin(dummy_array<T, Size> const & collection) 
{ 
    return dummy_array_const_iterator<T, Size>(collection, 0); 
} 

template <typename T, size_t const Size> 
inline dummy_array_const_iterator<T, Size> end(dummy_array<T, Size> const & collection) 
{ 
    return dummy_array_const_iterator<T, Size>(collection, collection.GetSize()); 
}

int main(int nArgc, char** argv)
{
    dummy_array<int, 10> arr;

    for (auto&& e : arr) 
    { 
        std::cout << e << std::endl; 
        e = 100;    // PROBLEM
    } 

    const dummy_array<int, 10> arr2;

    for (auto&& e : arr2)   // ERROR HERE
    { 
        std::cout << e << std::endl; 
    } 
}

Now, the error is pointing at the line 现在,错误指向该行

T & operator* ()

stating 说明

'return': cannot convert from 'const T' to 'T &'" 'return':无法从'const T'转换为'T&'”

...which is raised from my range based for loop on arr2 . ...这是基于arr2for循环从我的范围中提出的。

Why is the compiler choosing the none-constant version of operator*()? 为何编译器选择operator*()?非恒定版本operator*()? . I have looked at this for a long time; 我已经看了很长时间了; I think its because it thinks that the object on which it is calling this operator is not constant: this should be a dummy_array_const_iterator . 我认为这是因为它认为调用此运算符的对象不是常量:这应该是dummy_array_const_iterator But, this object has been declared to be constant via 但是,此对象已通过声明为常量

template <typename T, size_t const Size> 
using dummy_array_const_iterator =  const dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;

...so I really don't understand what is happening. ...所以我真的不明白发生了什么。 Can someone please clarify? 有人可以澄清一下吗?

TIA TIA

I found a way to enable T& operator*() only when C is not constant: 我找到了一种仅在C不恒定的情况下启用T& operator*()

    template <class Tp = T>
    typename std::enable_if<std::is_const<C>::value, Tp>::type const& operator* () const 
    { 
        return collection.GetAt(index); 
    }

    template <class Tp = T>
    typename std::enable_if<!std::is_const<C>::value, Tp>::type & operator* () const 
    { 
        return collection.GetAt(index); 
    }

I have no idea about the syntax (which I get from https://stackoverflow.com/a/26678178 ) 我不知道语法(我从https://stackoverflow.com/a/26678178获得)

dummy_array_const_iterator::operator * should always return T const & regardless of constness of the iterator object itself. dummy_array_const_iterator::operator *始终应返回T const &而与迭代器对象本身的T const &无关。

The easiest way to achieve this is probably just to declare it with T const as underlying iterator value type: 实现此目的的最简单方法可能只是使用T const声明它为基础迭代器值类型:

template <typename T, size_t const Size> 
using dummy_array_const_iterator = dummy_array_iterator_type<T const, dummy_array<T, Size> const, Size>;

Since you are returning the iterator by value its constness can be easily lost by c++ type deduction rules and just declaring dummy_array_const_iterator as alias to const dummy_array_iterator_type is not enough. 由于您dummy_array_const_iterator值返回迭代器,因此C ++类型推导规则很容易失去const dummy_array_iterator_type ,仅将dummy_array_const_iterator声明为const dummy_array_iterator_type别名是不够的。 ie the following fails: 即以下失败:

#include <type_traits>

struct I { };
using C = I const;
C begin();

int bar()
{
    auto x = begin(); // type of x is deduced as I
    static_assert(std::is_same<I, decltype(x)>::value, "same"); // PASS
    static_assert(std::is_same<decltype(begin()), decltype(x)>::value, "same"); // ERROR
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM