【问题标题】:template parameter for const iterator instead of iterator用于 const 迭代器而不是迭代器的模板参数
【发布时间】:2015-12-09 05:56:53
【问题描述】:

注意:我已经根据从练习中学到的经验以及此页面上的答案和 cmets 编写了一个“食谱”,请参阅 http://www.codeproject.com/Tips/1029941/Python-like-enumeration-in-Cplusplus

我正在玩弄 C++11 为 C++03 带来的扩展。我希望能够使用以下代码迭代容器:

int main()
{
    std::list<int> list = { 1, 2, 3, 4, 5, 6, 7 };
    for (auto x : enumerated(list))
        cout << x.first << " " << x.second << endl;
    for (auto x : const_enumerated(list))
        cout << x.first << " " << x.second << endl;
}

第一次迭代有 x 可修改,而第二次尝试修改 x 会导致编译错误。我有一些适用于非常量情况的东西:

template <typename Container>
class EnumerationAdaptor
{
public:
    EnumerationAdaptor(Container& container) : container_(container) {}
    EnumIter<typename Container::iterator> begin() const { return container_.begin(); }
    EnumIter<typename Container::iterator> end() const { return container_.end(); }

private:
    Container& container_;
};

template <typename Container>
EnumerationAdaptor<Container> enumerated(Container& container) { return container; }

template <typename Container>
EnumerationAdaptor<const Container> const_enumerated(const Container& container) { return container; }

非常量情况使用EnumIter&lt;std::list&lt;...&gt;::iterator&gt;,根据需要,我试图让const情况使用EnumIter&lt;std::list&lt;...&gt;::const_iterator&gt;作为begin()end()的返回类型。好像我需要 decltype:

template <typename Container>
class EnumerationAdaptor
{
public:
    EnumerationAdaptor(Container& container) : container_(container) {}
    EnumIter<decltype(Container().begin())> begin() const { return container_.begin(); }
    EnumIter<decltype(Container().end())> end() const { return container_.end(); }  // *** compile error (see below)

private:
    Container& container_;
};

但我在 Visual Studio 2015 Express 中遇到编译错误:

Error   C2440   'return': cannot convert from 
'std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>' 
to  
 'EnumIter<std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>>'
[in c:\users\...\enumeratedcpp.cpp line 46, which is line marked ***]

这表明我在使用 decltype 时做错了,因为编译器正在寻找非常量 begin()。有没有办法来解决这个问题?

编辑:即使使用简单的 EnumIter,问题也是一样的:

template <typename Iter>
class EnumIter
{
public:
    EnumIter(Iter begin) : iter_(begin) {}

    EnumIter& operator++()
    {
        return *this;
    }

    bool operator!=(const EnumIter& rhs)
    {
        return iter_ != rhs.iter_; // or self.index_ != rhs.index_;
    }

    int operator*() const
    {
        return index_;
    }

private:
    Iter iter_;
    int index_ = 0;
};

【问题讨论】:

  • 使用单独的ConstEnumerationAdaptor 调用cbegincend 可能更容易解决这个问题
  • 否则,请尝试将 Container()(在 decltype 内)替换为 std::declval&lt;Container&amp;&gt;()(来自
  • 其实我觉得问题可能出在EnumIter&lt;&gt;
  • @Schollii 您确实需要declval 中的&amp;,因为您将容器作为左值保存,并且您不想使用错误的类型以防出现右值引用-合格的重载。
  • using iterator_type = std::conditional_t&lt;std::is_const&lt;Container&gt;{}, typename Container::const_iterator, typename Container::iterator&gt;.

标签: c++ templates c++11 iterator constants


【解决方案1】:

这个表达式有一个问题:

decltype(Container().begin())

这就是Container() 仅在Container 恰好是默认可构造的情况下才有效。这无缘无故地限制了您的课程的可用性。 (还有一个较小的问题是这不适用于原始数组,但这是另一个练习)。

除此之外,代码对类类型完全有效。来自[expr.type.conv]:

表达式 T(),其中 T 是非数组完整对象的简单类型说明符或类型名称说明符 type 或(可能是 cv 限定的)void 类型,创建指定类型的纯右值 [...]

所以如果Containerconst list&lt;int&gt;,那么整个表达式的类型应该是list&lt;int&gt;::const_iterator。如果 MSVC 给了你别的东西,那就是一个错误。

也就是说,我们确实应该解决默认可构造性问题。这就是std::declval 的用武之地:

decltype(std::declval<Container&>().begin())

这不会对Container 施加任何限制,也许MSVC 会正确处理这个问题。

【讨论】:

  • "第二,构造函数调用的类型永远不是 cv 限定的。所以decltype(T()) 有效的地方与std::remove_cv_t&lt;T&gt; 相同。" [expr.type.conv]/p2 似乎提出了不同的建议。还有 GCC 和 Clang preserves cv-qualification。
  • @T.C.是的,第一次错过了非课堂部分。嗯。那么为什么OP的表达式不起作用呢?
  • MSVC 错误,据我所知。
  • 有见地,谢谢。好吧,如果是真的,我是因为编译器中的一个错误而学到了一些东西:) 顺便说一句,至少在 VC++ 2015 中,declval 中不需要 & ,该函数的文档似乎也不需要它,也没有用于cppreference.com 上的示例,但你和 sp2danny 都使用了它,原因是什么?
  • @Schollii declval&lt;T&gt;() 给你一个右值引用,declval&lt;T&amp;&gt;() 给你一个左值引用。如果有一个begin() &amp; 和一个begin() &amp;&amp;,这两个可以给你不同的类型。
猜你喜欢
  • 2014-10-05
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 2017-03-12
  • 1970-01-01
  • 1970-01-01
  • 2015-09-04
  • 1970-01-01
相关资源
最近更新 更多