不幸的是,我还没有能够降低这个问题的复杂性,因此它的代码相当长。问题的核心是我正在运行的测试。
TEST(Iterator, enumerator_rvalue)
{
std::size_t index1 = 5;
for (auto item : nr::enumerate(nr::range<std::size_t>(5, 27))) {
ASSERT_EQ(index1 - 5, item.index);
ASSERT_EQ(index1++, item.value);
}
ASSERT_EQ(index1, 27);
}
在使用GCC 5和6进行编译时,for
循环的第二次迭代中的断言失败但是对Clang 3.6,3.7,3.8(在Travis CI上测试)和MSVC 2015(在{ {3}},测试用例名称为Iterator.enumerator_rvalue
)。
我已将所有必要的代码复制到一个文件中,以便在Coliru上复制它,并添加了一些小版本和ASSERT_EQ()
描述代码正在做什么:nr::range
是一个迭代器,它只是从start
迭代到end-1
。 nr::enumerate()
生成一个nr::enumerator
,接受Iterable
的右值。因此,在这种情况下,nr::range
对象将移动到nr::enumerator::iter
成员。
nr::enumerate(nr::range<std::size_t>(5, 27))
nr::enumerator
现在简单地包装迭代器和nr::range
对象中nr::enumerator::item
所产生的值,以将其与索引相关联。
这是在Clang的for循环中打印的前两行:
index1=5, item.index:0, item.value:5
index1=6, item.index:1, item.value:6
使用GCC,它是
index1=5, item.index:0, item.value:5
index1=6, item.index:1, item.value:1
现在IMO最有趣的部分是,当我评论(也就是删除)ASSERT_EQ()
语句时,GCC还会打印正确的值
index1=5, item.index:0, item.value:5
index1=5, item.index:1, item.value:6
现在问题:这是GCC错误还是我触发了未定义的行为?
我也考虑过我自己的ASSERT_EQ()
宏可能会疯狂,但我无法找到它的问题,而这也无法解释Travis的googletest失败和AppVeyor。
编辑:我将在此处包含代码,以防Coliru链接中断(@NathanOliver)。只需复制并粘贴到文件中即可(g|clang)++ -std=c++(11|14) main.cpp && ./a.out
#include <iostream>
// Copyright (c) 2016 Niklas Rosenstein
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// @file nr/iterator.h
// @created 2016-01-21
// @lastmodified 2016-06-06
//#pragma once
#include <cassert>
// ---------------------------------------------------------------------------
// @macro NR_ITERATOR_NOTYPETRAITS
//
// If this macro is defined, #<type_traits> will not but used. This requires
// the implementor of the #nr::iterator interface to manually specify the
// datatype that the `operator * ()` (dereference operator) will return
// as `yield_type`.
// ---------------------------------------------------------------------------
#ifndef NR_ITERATOR_NOTYPETRAITS
#include <type_traits>
#endif
namespace nr {
// ---------------------------------------------------------------------------
// The #self_iterator is an adapter class from a new style iterator
// interface to the C++ iterator interface. It can be subclassed to
// implement iterator objects with the following interface:
//
// - `at_end() const`
// - `next()`
// - `X operator * () const`
//
// The first two methods will be used by the #self_iterator methods
// #operator++(), #operator!=() and #operator bool() to adapt to the
// C++ iterator interface. The dereference operator is required by
// the C++ iterator interface directly.
//
// This class should be used when implementing a simple iterator.
// However, the calls to #begin() and #end() will cause a copy of
// the iterator to be created, thus it will be problematic if your
// iterator allocates and frees resources automatically.
//
// For such cases, use the #iterator instead.
// ---------------------------------------------------------------------------
template <class T>
class self_iterator
{
public:
// -------------------------------------------------------------------------
// Returns false if the iterator is at its end, true otherwise.
// This method calls #at_end(). Note that #at_end() must be declared
// `const` if this operator is used.
// -------------------------------------------------------------------------
inline operator bool () const {
return !static_cast<T const*>(this)->at_end();
}
// -------------------------------------------------------------------------
// Used by the standard C++ iterator interface. Calls `at_end()` to
// check if the end of the iterator was reached.
// -------------------------------------------------------------------------
inline bool operator != (self_iterator const&) {
return !static_cast<T*>(this)->at_end();
}
// -------------------------------------------------------------------------
// Increment operator. Calls `next()` to advance to the next element.
// -------------------------------------------------------------------------
self_iterator& operator ++ () {
static_cast<T*>(this)->next();
return *this;
}
// -------------------------------------------------------------------------
// C++ iterator interface.
// -------------------------------------------------------------------------
// @{
T& begin() { return *static_cast<T*>(this); }
T& end() { return *static_cast<T*>(this); }
// @}
};
// ---------------------------------------------------------------------------
// This class represents an adapter of the C++ iterator interface
// for new style iterator classes.
// ---------------------------------------------------------------------------
template <typename T>
class iterator_adapter
{
// -------------------------------------------------------------------------
// Pointer to the iterator implementation.
// -------------------------------------------------------------------------
T* itimpl;
public:
// -------------------------------------------------------------------------
// @typedef yield_type
//
// This type represents the datatype that is yielded by the iterator.
// If #NR_ITERATOR_NOTYPETRAITS is defined, #<type_traits> will not be
// used and #T must provide the #yield_type instead.
// -------------------------------------------------------------------------
#ifdef NR_ITERATOR_NOTYPETRAITS
typedef typename T::yield_type yield_type;
#else
typedef typename std::result_of<decltype(&T::operator *)(T)>::type yield_type;
#endif
// -------------------------------------------------------------------------
// Passing nullptr to this constructor represents creates a
// sentinel iterator that marks the end of the iteration.
// -------------------------------------------------------------------------
inline iterator_adapter(T* itimpl) : itimpl(nullptr)
{
if (itimpl && !itimpl->at_end()) {
this->itimpl = itimpl;
}
}
// -------------------------------------------------------------------------
// Calls `next()` and `at_end()` on the iterator implementation
// to advance to the next item and check if the end of the iteration
// has been reached.
// -------------------------------------------------------------------------
inline iterator_adapter& operator ++ ()
{
assert(this->itimpl != nullptr);
this->itimpl->next();
if (this->itimpl->at_end()) {
this->itimpl = nullptr;
}
return *this;
}
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
inline bool operator != (iterator_adapter const& other) const
{
return this->itimpl != other.itimpl;
}
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
inline yield_type operator * () const
{
assert(this->itimpl != nullptr);
return this->itimpl->operator * ();
}
};
// ---------------------------------------------------------------------------
// This is an extension of the #self_iterator that uses an
// #iterator_adapter class for #begin() and #end() instead.
// It should be used when employing an iterator that allocates and
// frees resource dynamically.
// ---------------------------------------------------------------------------
template <class T>
class iterator : public self_iterator<T>
{
public:
// -------------------------------------------------------------------------
// Returns the #iterator_adapter for this iterator implementation.
// It does not really mark the begin of the iteration, rather the
// current state of the iterator.
// -------------------------------------------------------------------------
inline iterator_adapter<T> begin() { return iterator_adapter<T>(static_cast<T*>(this)); }
// -------------------------------------------------------------------------
// Returns the sentinel #iterator_adapter that marks the end of
// the iteration.
// -------------------------------------------------------------------------
inline iterator_adapter<T> end() const { return iterator_adapter<T>(nullptr); }
// -------------------------------------------------------------------------
// Returns false if the iterator is at its end, true otherwise.
// This method calls #at_end(). Note that #at_end() must be declared
// `const` if this operator is used.
// -------------------------------------------------------------------------
inline operator bool () const {
return !static_cast<T const*>(this)->at_end();
}
};
} // namespace nr
// Copyright (c) 2016 Niklas Rosenstein
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// @file <nr/iterator/range.h>
// @created 2016-07-02
// @brief Provides the #nr::range iterator class.
//#pragma once
//#include "../iterator.h"
#include <iterator>
namespace nr {
// ---------------------------------------------------------------------------
// An implementation of a range iterator using the #nr::iterator
// interface. Supports an arbitrary integer dataype that can be
// specified with the template parameter #T.
// ---------------------------------------------------------------------------
template <typename T>
class range : public iterator<range<T>>
{
T curr, max, step;
// -------------------------------------------------------------------------
// Simple function to retrieve the sign of a function.
// -------------------------------------------------------------------------
static int sign(T val) { return (T(0) < val) - (val < T(0)); }
public:
// -------------------------------------------------------------------------
// Create a range iterator starting from zero up excluding #max.
// -------------------------------------------------------------------------
inline range(T max) : curr(0), max(max), step(1) { std::cout << "range()\n"; }
// -------------------------------------------------------------------------
// Create a range iterator starting from #min up to excluding #max
// taking the specified #step each turn (must not be zero and the
// sign must match the direction of the iteration).
// -------------------------------------------------------------------------
inline range(T min, T max, T step = 1) : curr(min), max(max), step(step)
{
assert(step != 0 && sign(step) == sign(max - min));
std::cout << "range()\n";
}
inline ~range() {
std::cout << "~range()\n";
}
public: // nr::iterator overrides
inline bool at_end() const { return this->curr >= this->max; }
inline void next() { this->curr += step; }
inline T operator * () const { return this->curr; }
};
} // namespace nr
// Copyright (c) 2016 Niklas Rosenstein
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/*!
* @file <nr/iterator/enumerate.h>
* @created 2016-12-08
* @brief Python's `enumerate()` like iterator.
*/
//#pragma once
//#include "../iterator.h"
namespace nr {
/*!
* Implementation of the iterator behind #enumerate().
*/
template <class Iterable>
class enumerator : public nr::iterator<enumerator<Iterable>>
{
Iterable iter;
std::size_t index;
decltype(std::begin(iter)) iter_begin;
decltype(std::end(iter)) const iter_end;
public: // types
struct item
{
std::size_t index;
decltype(*iter_begin) value;
};
public:
enumerator(Iterable&& iter_)
: iter(std::move(iter_)), index(0), iter_begin(std::begin(iter_))
, iter_end(std::end(iter_)) {}
bool at_end() const { return !(this->iter_begin != this->iter_end); }
void next() { ++this->iter_begin; ++this->index; }
item operator * () const { return {this->index, *this->iter_begin}; }
};
/*!
* Helper for lvalues passed to #enumerate().
*/
template <typename Iterable>
class enumerator_lvalue_wrapper
{
Iterable& iter;
public:
enumerator_lvalue_wrapper(Iterable& iter_) : iter(iter_) {}
enumerator_lvalue_wrapper(enumerator_lvalue_wrapper&& other)
: iter(other.iter) {}
auto begin() -> decltype(std::begin(iter)) { return std::begin(iter); }
auto end() -> decltype(std::end(iter)) { return std::end(iter); }
auto begin() const -> decltype(std::begin(iter)) { return std::begin(iter); }
auto end() const -> decltype(std::end(iter)) { return std::end(iter); }
};
/*!
* Enumerate over an iterator.
*/
template <typename Iterable>
auto enumerate(Iterable& iter) -> enumerator<enumerator_lvalue_wrapper<Iterable>>
{
std::cout << "enumerate(ref)\n";
return {enumerator_lvalue_wrapper<Iterable>(iter)};
}
/*!
* Enumerate over an rvalue iterator.
*/
template <typename Iterable>
auto enumerate(Iterable&& iter) -> enumerator<Iterable>
{
std::cout << "enumerate(move)\n";
return {std::move(iter)};
}
} // namespace nr
template <typename T>
static void _assert_eq(T const& a, T const& b, char const* msg) {
if (a != b) {
std::cerr << msg << std::endl;
std::cerr << a << "!=" << b << std::endl;
throw std::runtime_error("bad");
}
}
#define ASSERT_EQ(a, b) _assert_eq<decltype(a)>((a), (b), #a " == " #b)
//TEST(Iterator, enumerator_rvalue)
int main()
{
std::size_t index1 = 5;
for (auto item : nr::enumerate(nr::range<std::size_t>(5, 27))) {
std::cout << "index1=" << index1 << ", item.index:" << item.index << ", item.value:" << item.value << std::endl;
ASSERT_EQ(index1 - 5, item.index);
ASSERT_EQ(index1++, item.value);
}
ASSERT_EQ(index1, 27);
}
答案 0 :(得分:4)
问题出在您的enumerator
构造函数中。
enumerator(Iterable&& iter_)
: iter(std::move(iter_)), index(0), iter_begin(std::begin(iter_))
, iter_end(std::end(iter_)) {}
这里您将迭代器iter_begin
和iter_end
设置为iter_
参数,但在外部它是一个临时的,它将在初始化后直接过期。这就是导致崩溃的原因。相反,您应该将迭代器设置为iter
数据成员,该成员是具有移动状态的新对象:
enumerator(Iterable&& iter_)
: iter(std::move(iter_)), index(0), iter_begin(std::begin(iter))
, iter_end(std::end(iter)) {}
您的程序现在运行完成:http://coliru.stacked-crooked.com/a/2ddf4ace518ac68e