【问题标题】:STL const_iterator cast -- compiler differencesSTL const_iterator cast -- 编译器差异
【发布时间】:2014-09-01 03:01:21
【问题描述】:

我正在将一大段代码从 gcc 移植到 Visual Studio 2013。以下代码示例在 gcc 4.4 上运行良好(!),但在 VS2013 上编译 begin()end() 失败:

错误 C2440: '' : 无法从 'unsigned char *' 转换为 'std::_Vector_const_iterator>>'

class foo {
    unsigned char* value;
    int length;

    std::vector<unsigned char>::const_iterator begin();
    std::vector<unsigned char>::const_iterator end();
};

std::vector<unsigned char>::const_iterator foo::begin() {
    return std::vector<unsigned char>::const_iterator(value);
}

std::vector<unsigned char>::const_iterator foo::end() {
    return std::vector<unsigned char>::const_iterator(value + length);
}

鉴于我不想重写整个事情,有没有一种可移植的方式来创建这些 const_iterators?

【问题讨论】:

  • 抱歉,您利用了 VS2013 中不存在的 gcc 实现细节。 const_iterator 的定义不在您的控制范围内,它不包括获取任意指针的能力。
  • 你能把类型从std::vector&lt;unsigned char*&gt;::iterator改成const unsigned char*或者自定义迭代器吗?
  • 或者,如果value总是来自一个向量,你能把它改成std::vector&lt;unsigned char*&gt;::iterator value;吗?

标签: c++ gcc stl visual-studio-2013 const-iterator


【解决方案1】:

没有可移植的方式来做你正在尝试的事情,因为没有要求 (const_)iterator 可以从指向基础值类型的指针构造。 libstdc++ 恰好提供了这样的构造函数,但 VS 标准库实现却没有。相反,它的(const_)iterator 构造函数接受一个指向底层值类型的指针和一个指向容器本身的指针,它在调试构建期间使用它来执行额外的验证。

最简单的解决方案是将std::vector&lt;unsigned char&gt;::const_iterator 替换为unsigned char const *。原始指针属于 RandomAccessIterator 类别,与vector::(const_)iterators 相同。

unsigned char const *foo::begin() {
    return value;
}

unsigned char const *foo::end() {
    return value + length;
}

如果您需要迭代器是类类型,那么您需要创建一个自定义迭代器。虽然这可以从头开始,但使用 Boost.IteratorFacade 会容易得多,它会提供大量用于构建自定义迭代器的必要样板。

#include <boost/iterator/iterator_facade.hpp>

struct const_foo_iterator : boost::iterator_facade<const_foo_iterator,
                                                    unsigned char const,
                                                    boost::random_access_traversal_tag>
{
  const_foo_iterator() = default;
  const_foo_iterator(unsigned char const *iter) : iter(iter) {}
private:
    friend class boost::iterator_core_access;

    void increment() { ++iter; }
    void decrement() { --iter; }
    void advance(std::ptrdiff_t n) { iter += n; }

    std::ptrdiff_t distance_to(const_foo_iterator const& other) const
    { return iter - other.iter; }

    bool equal(const_foo_iterator const& other) const
    { return this->iter == other.iter; }

    unsigned char const& dereference() const { return *iter; }
    unsigned char const* iter = nullptr;
};

const_foo_iterator foo::begin() {
    return value;
}

const_foo_iterator foo::end() {
    return value + length;
}

static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::value_type,
                           unsigned char>::value, "value_type");
static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::pointer,
                           unsigned char const *>::value, "pointer");
static_assert(std::is_same<std::iterator_traits<const_foo_iterator>::iterator_category,
                           std::random_access_iterator_tag>::value, "iterator_category");

Live demo

【讨论】:

  • 谢谢你,这正是我所害怕的——没有越狱卡。该示例在大量代码中显示了冲突的基本要素,这些代码不是我编写的,并且希望避免对其进行大手术。
  • 这个答案再次假设调用代码足够通用,不关心迭代器的实际类型。但是,如果代码的其余部分到处都包含像 for (std::vector&lt;unsigned char&gt;::const_iterator iter = my_foo.begin(); iter != my_foo.end(); ++iter) 这样的行怎么办?
  • @ChristianHackl 是的,没有办法解决
  • @ChristianHackl 这是他们发明auto 的一个很好的理由。
【解决方案2】:

这取决于begin()end() 函数的使用方式。如果这些函数的调用者实际上期望 std::vector&lt;unsigned char&gt;::const_iterator(而不是足够通用而不关心确切的类型),那么您必须寻找不同的解决方案。

您可以尝试在其实现中实现解决方法,而不是更改类的接口。需要什么value 指针?还有谁可以访问它?如果它是私有成员,则只需将其替换为实际的 std::vector&lt;unsigned char&gt;,并使用其 data() 成员函数 (C++11) 或 &amp;value[0] (pre C++11) 将指针传递到预期的位置.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-10
    • 2017-07-26
    • 1970-01-01
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 2015-05-05
    相关资源
    最近更新 更多