键入std :: vector的安全索引值

时间:2018-03-20 14:51:10

标签: c++ stdvector

我有从不同的常量 STL 向量收集索引值的类。问题是,即使这些向量的内容不同并且它们具有不同的目的,它们的索引也是std::size_t类型,因此可能错误地使用为一个向量存储的索引来访问另一个向量的元素。当索引不与正确的向量一起使用时,是否可以更改代码以便产生编译时错误?

代码示例:

#include <iostream>
#include <string>
#include <vector>

struct Named
{
    std::string name;
};

struct Cat : Named { };
struct Dog : Named { };

struct Range
{
    std::size_t start;
    std::size_t end;
};

struct AnimalHouse
{
    std::vector< Cat > cats;
    std::vector< Dog > dogs;
};

int main( )
{
    AnimalHouse house;
    Range cat_with_name_starting_with_a;
    Range dogs_with_name_starting_with_b;

    // ...some initialization code here...

    for( auto i = cat_with_name_starting_with_a.start;
         i < cat_with_name_starting_with_a.end;
         ++i )
    {
        std::cout << house.cats[ i ].name << std::endl;
    }

    for( auto i = dogs_with_name_starting_with_b.start;
         i < dogs_with_name_starting_with_b.end;
         ++i )
    {
        // bad copy paste but no compilation error
        std::cout << house.cats[ i ].name << std::endl; 
    }

    return 0;
}

免责声明:请不要过多关注示例本身,我知道这是愚蠢的,只是为了得到这个想法。

1 个答案:

答案 0 :(得分:1)

以下是对我的评论进行跟进的尝试 当然,根据用例的情况,有很多空间可以更改其工作方式的细节,这种方式对我来说似乎是合理的。

#include <iostream>
#include <vector>

template <typename T>
struct Range {
    Range(T& vec, std::size_t start, std::size_t end) :
        m_vector(vec),
        m_start(start),
        m_end(end),
        m_size(end-start+1) {}

    auto begin() {
        auto it = m_vector.begin();
        std::advance(it, m_start);
        return it;
    }
    auto end() {
        auto it = m_vector.begin();
        std::advance(it, m_end + 1);
        return it;
    }

    std::size_t size() {
        return m_size;
    }

    void update(std::size_t start, std::size_t end) {
        m_start = start;
        m_end = end;
        m_size = end - start + 1;
    }

    Range copy(T& other_vec) {
        return Range(other_vec, m_start, m_end);
    }

    typename T::reference operator[](std::size_t index) {
        return m_vector[m_start + index];
    }

    private:
    T& m_vector;
    std::size_t m_start, m_end, m_size;
};

// This can be used if c++17 is not supported, to avoid
// having to specify template parameters
template <typename T>
Range<T> make_range(T& t, std::size_t start, std::size_t end) {
    return Range<T>(t, start, end);
}

int main() {
    std::vector<int> v1 {1, 2, 3, 4, 5};
    std::vector<double> v2 {0.5, 1., 1.5, 2., 2.5};

    Range more_then_2(v1, 1, 4); // Only works in c++17 or later
    auto more_then_1 = make_range(v2, 2, 4);

    for (auto v : more_then_2)
        std::cout << v << ' ';

    std::cout << std::endl;

    for (auto v : more_then_1)
        std::cout << v << ' ';

    std::cout << std::endl;

    more_then_2.update(2,4);

    for (auto v : more_then_2)
        std::cout << v << ' ';

    std::cout << std::endl;

    auto v3 = v1;
    auto more_then_2_copy = more_then_2.copy(v3);

    for (unsigned i=0; i < more_then_2_copy.size(); ++i)
        std::cout << more_then_2_copy[i] << ' ';

    return 0;
}