Related
Why doesn't std::ranges::common_view compile in the code below?
#include <ranges>
#include <vector>
#include <algorithm>
template <class T>
struct IteratorSentinel {};
template <class T>
class Iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
Iterator() = default;
Iterator(const Iterator&) = delete;
Iterator& operator = (const Iterator&) = delete;
Iterator(Iterator&& other) = default;
Iterator& operator = (Iterator&& other) = default;
T* operator-> () { return cur(); }
T& operator* () { return *cur(); }
T& operator* () const { return *cur(); };
bool operator== (const IteratorSentinel<T>&) const noexcept;
Iterator& operator++ ();
void operator++ (int);
private:
T* cur() const
{
return pCur;
}
T* pCur = nullptr;
};
static_assert(std::input_iterator<Iterator<int>>);
template <class T>
auto make_range()
{
return std::ranges::subrange(Iterator<T>(), IteratorSentinel<T>{});
}
int main()
{
auto r = make_range<int>();
auto cr = std::ranges::common_view{ r };
std::vector<int> v;
std::copy(cr.begin(), cr.end(), std::back_inserter(v));
return 0;
}
Can't figure out what template parameter does it require.
MSVC2022 error (/std:c++latest):
error C2641: cannot deduce template arguments for 'std::ranges::common_view'
error C2893: Failed to specialize function template 'std::ranges::common_view<_Vw> std::ranges::common_view(_Vw) noexcept(<expr>)'
GCC12 errors:
prog.cc: In function 'int main()':
prog.cc:67:43: error: class template argument deduction failed:
67 | auto cr = std::ranges::common_view{ r };
| ^
prog.cc:67:43: error: no matching function for call to 'common_view(std::ranges::subrange<Iterator<int>, IteratorSentinel<int>, std::ranges::subrange_kind::unsized>&)'
In file included from prog.cc:1:
/opt/wandbox/gcc-12.1.0/include/c++/12.1.0/ranges:3724:7: note: candidate: 'template<class _Vp> common_view(_Vp)-> std::ranges::common_view<_Vp>'
3724 | common_view(_Vp __r)
| ^~~~~~~~~~~
Your range is built out of an iterator/sentinel pair. The definition of a common range is a range where the sentinel type is an iterator. So the range itself is not a common range.
common_view can generate a common range from a non-common range. Which means that it will have to create two iterators. And since it starts the process with only one iterator, that means that, at some point, it must copy that iterator (thus creating two usable iterators).
Which it can't do because your iterator is non-copyable. Which is why common_view has an explicit requirement that the iterator is copyable.
I'm working on an Advent of Code challenge (2021 day 18). Just as a test I tried compiling it on different compilers. While GCC (11.2) and MSVC (19.30) think it's fine, Clang (13.0.0) throws a list of errors. link to compiler explorer
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/alloc_traits.h:514:4: error: no matching function for call to 'construct_at'
std::construct_at(__p, std::forward<_Args>(__args)...);
^~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/vector.tcc:115:21: note: in instantiation of function template specialization 'std::allocator_traits<std::allocator<RegularNumber>>::construct<RegularNumber, int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>' requested here
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
^
<source>:115:19: note: in instantiation of function template specialization 'std::vector<RegularNumber>::emplace_back<int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>' requested here
regNumVec.emplace_back(*it - '0', leftRegNumIdx,
^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_construct.h:94:5: note: candidate template ignored: substitution failure [with _Tp = RegularNumber, _Args = <int, Named<int, index_for_vector_of_RegularNumber> &, Named<int, index_for_vector_of_RegularNumber>>]: no matching constructor for initialization of 'RegularNumber'
construct_at(_Tp* __location, _Args&&... __args)
^
What doesn't Clang understand what seems fine according to the other two? Is this a bug, or my mistake?
Here's the "minimalized" code:
#include <algorithm>
#include <concepts>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
template <typename Type, typename Description>
class Named {
public:
using UnderlyingType = Type;
// clang-format off
constexpr Named()
noexcept(std::is_nothrow_default_constructible_v<Type>)
requires std::default_initializable<Type>
= default;
explicit constexpr Named(Type const &val)
noexcept(std::is_nothrow_copy_constructible_v<Type>)
requires std::copyable<Type>
: val_{val}
{}
explicit constexpr Named(Type&& val)
noexcept(std::is_nothrow_move_constructible_v<Type>)
requires std::movable<Type>
: val_{std::move(val)}
{}
constexpr operator Type() const&
noexcept(std::is_nothrow_copy_constructible_v<Type>)
requires std::copyable<Type>
{
return val_;
}
constexpr operator Type() const&&
noexcept(std::is_nothrow_move_constructible_v<Type>) // move assignable?
requires std::movable<Type>
{
return std::move(val_);
}
private:
Type val_;
};
// clang-format on
template <typename T>
auto readinputdata(std::istream &&is) {
auto data{std::vector<T>{}};
copy(std::istream_iterator<T>{is}, std::istream_iterator<T>{},
back_inserter(data));
return data;
}
static constexpr auto testData{
"[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]\n"
"[[[5,[2,8]],4],[5,[[9,9],0]]]\n"
"[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]\n"
"[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]\n"
"[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]\n"
"[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]\n"
"[[[[5,4],[7,7]],8],[[8,3],8]]\n"
"[[9,3],[[9,9],[6,[4,9]]]]\n"
"[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]\n"
"[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]\n"};
struct index_for_vector_of_Pair;
using PairIndex = Named<int, index_for_vector_of_Pair>;
struct index_for_vector_of_RegularNumber;
using RegNumIndex = Named<int, index_for_vector_of_RegularNumber>;
struct RegularNumber {
using IndexType = RegNumIndex;
int value{};
RegNumIndex leftIdx{}, rightIdx{};
};
using IndexVariant = std::variant<PairIndex, RegNumIndex>;
struct Pair {
using IndexType = PairIndex;
PairIndex parent{};
IndexVariant leftIdx{}, rightIdx{};
};
static auto pairVec{std::vector<Pair>{}};
static auto regNumVec{std::vector<RegularNumber>{}};
// used for converting the input strings to pairs
static auto leftRegNumIdx{RegNumIndex{-1}};
static constexpr auto invalid_index{-1};
template <typename Type>
auto get_last_index(std::vector<Type> const &vec) {
using Index = typename Type::IndexType;
return Index{static_cast<typename Index::UnderlyingType>(size(vec))};
}
auto generate_pair(std::string::const_iterator &it, PairIndex parent)
-> PairIndex;
auto generate_side(std::string::const_iterator &it, PairIndex pairIdx)
-> IndexVariant {
if (*it == '[') {
return generate_pair(it, pairIdx);
} else {
regNumVec.emplace_back(*it - '0', leftRegNumIdx, // error on this line
RegNumIndex{invalid_index});
auto const litIdx{get_last_index(regNumVec)};
if (leftRegNumIdx != invalid_index) {
regNumVec[leftRegNumIdx].rightIdx = litIdx;
}
leftRegNumIdx = litIdx;
return litIdx;
};
}
auto generate_pair(std::string::const_iterator &it,
PairIndex parent = PairIndex{invalid_index}) -> PairIndex {
pairVec.emplace_back();
auto const pairIdx{get_last_index(pairVec)};
auto &pair{pairVec.back()};
pair.parent = parent;
++it; // skip [
pair.leftIdx = generate_side(it, pairIdx);
++it; // skip comma
pair.rightIdx = generate_side(it, pairIdx);
++it; // skip ]
return pairIdx;
}
int main() {
auto const snailFishNumbers{readinputdata<std::string>( //
std::stringstream{testData} //
)};
auto const outerPairIdxs{[&] {
auto outerPairIdxs{std::vector<PairIndex>{}};
outerPairIdxs.reserve(size(snailFishNumbers));
transform(cbegin(snailFishNumbers), cend(snailFishNumbers),
back_inserter(outerPairIdxs), [](auto const &str) {
auto it{cbegin(str)}; // need lvalue
return generate_pair(it);
});
return outerPairIdxs;
}()};
return 0;
}
Your type is an aggregate
struct RegularNumber {
using IndexType = RegNumIndex;
int value{};
RegNumIndex leftIdx{}, rightIdx{};
};
which construct_at tries to initialize with round parenthesis
::new(std::declval<void*>()) T(std::declval<Args>()...).
However, an aggregate needs a {} initializer, until the compiler implements P0960 Allow initializing aggregates from a parenthesized list of values
And Clang just isn't there yet. Compiler support for C++20
The following code compiles perfectly if:
I don't include <iostream> or
I name operator== as alp::operator==.
I suppose there is a problem with <iostream> and operator==, but I don't know what.
I compile the code with gcc 7.3.0, clang++-6.0 and goldbolt. Always the same error.
The problem is that the compiler is trying to cast the parameters of operator== to const_iterator, but why? (I suppose the compiler doesn't see my version of operator==, and looks for other versions).
#include <vector>
#include <iostream> // comment and compile
namespace alp{
template <typename It_base>
struct Iterator {
using const_iterator = Iterator<typename It_base::const_iterator>;
operator const_iterator() { return const_iterator{}; }
};
template <typename It_base>
bool operator==(const Iterator<It_base>& x, const Iterator<It_base>& y)
{ return true;}
}// namespace
struct Func{
int& operator()(int& p) const {return p;}
};
template <typename It, typename View>
struct View_iterator_base{
using return_type = decltype(View{}(*It{}));
using const_iterator =
View_iterator_base<std::vector<int>::const_iterator, Func>;
};
using view_it =
alp::Iterator<View_iterator_base<std::vector<int>::iterator, Func>>;
int main()
{
view_it p{};
view_it z{};
bool x = operator==(z, p); // only compiles if you remove <iostream>
bool y = alp::operator==(z,p); // always compile
}
Error message:
yy.cpp: In instantiation of ‘struct View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>’:
yy.cpp:9:73: required from ‘struct alp::Iterator<View_iterator_base<__gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func> >’
yy.cpp:44:29: required from here
yy.cpp:28:42: error: no match for call to ‘(Func) (const int&)’
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
yy.cpp:22:10: note: candidate: int& Func::operator()(int&) const <near match>
int& operator()(int& p) const {return p;}
^~~~~~~~
yy.cpp:22:10: note: conversion of argument 1 would be ill-formed:
yy.cpp:28:42: error: binding reference of type ‘int&’ to ‘const int’ discards qualifiers
using return_type = decltype(View{}(*It{}));
~~~~~~^~~~~~~
I've made a more minimal test case here: https://godbolt.org/z/QQonMG .
The relevant details are:
A using type alias does not instantiate a template. So for example:
template<bool b>
struct fail_if_true {
static_assert(!b, "template parameter must be false");
};
using fail_if_used = fail_if_true<true>;
will not cause a compile time error (if fail_if_used isn't used)
ADL also inspects template parameter classes. In this case, std::vector<int>::iterator is __gnu_cxx::__normal_iterator<const int*, std::vector<int> >, Func>, which has a std::vector<int> in it's template. So, operator== will check in the global namespace (always), alp (As alp::Iterator is in alp), __gnu_cxx and std.
Your View_iterator_base::const_iterator is invalid. View_iterator_base::const_interator::result_type is defined as decltype(Func{}(*std::vector<int>::const_iterator{})). std::vector<int>::const_iterator{} will be a vectors const iterator, so *std::vector<int>::const_iterator{} is a const int&. Func::operator() takes an int&, so this means that the expression is invalid. But it won't cause a compile time error if not used, for the reasons stated above. This means that your conversion operator is to an invalid type.
Since you don't define it as explicit, the conversion operator (To an invalid type) will be used to try and match it to the function parameters if they don't already match. Obviously this will finally instantiate the invalid type, so it will throw a compile time error.
My guess is that iostream includes string, which defines std::operator== for strings.
Here's an example without the std namespace: https://godbolt.org/z/-wlAmv
// Avoid including headers for testing without std::
template<class T> struct is_const { static constexpr const bool value = false; } template<class T> struct is_const<const T> { static constexpr const bool value = true; }
namespace with_another_equals {
struct T {};
bool operator==(const T&, const T&) {
return true;
}
}
namespace ns {
template<class T>
struct wrapper {
using invalid_wrapper = wrapper<typename T::invalid>;
operator invalid_wrapper() {}
};
template<class T>
bool operator==(const wrapper<T>&, const wrapper<T>&) {
return true;
}
}
template<class T>
struct with_invalid {
static_assert(!is_const<T>::value, "Invalid if const");
using invalid = with_invalid<const T>;
};
template<class T>
void test() {
using wrapped = ns::wrapper<with_invalid<T>>;
wrapped a;
wrapped b;
bool x = operator==(a, b);
bool y = ns::operator==(a, b);
}
template void test<int*>();
// Will compile if this line is commented out
template void test<with_another_equals::T>();
Note that just declaring operator const_iterator() should instantiate the type. But it doesn't because it is within templates. My guess is that it is optimised out (where it does compile because it's unused) before it can be checked to show that it can't compile (It doesn't even warn with -Wall -pedantic that it doesn't have a return statement in my example).
I would like to use BOOST_FOREACH with one of legacy container type in my code base which I cannot change.
I have the following methods defined on that type:
.length() returning the current number of elements in the container
.operator[](unsigned i) returning a reference to element under index i
I know that I need to make my container type to satisfy Single Pass Range Concept as the boost documentation suggest but I am somehow lost since I cannot modify the type.
Any hints/solutions will be useful.
EDIT
I tried to go with suggestion but this code
struct wrapper
{
struct iterator : std::iterator<std::forward_iterator_tag, ObservationReport>
{
iterator(ObservationReportList* p, int i) : pnt_(p), i_(i) {}
ObservationReport& operator*() {
return (*pnt_)[i_];
}
iterator& operator++() {
++i_;
return *this;
}
bool operator==(const iterator& r) const {
return i_ == r.i_;
}
bool operator!=(const iterator& r) const {
return i_ != r.i_;
}
ObservationReportList* pnt_;
int i_;
};
wrapper(ObservationReportList & n)
: begin_( boost::addressof(n), 0 )
, end_( boost::addressof(n), n.length() )
{}
iterator begin() { return begin_; }
iterator end() { return end_; }
iterator begin_, end_;
};
// usage
ObservationReportList reportList;
// filling reportList
BOOST_FOREACH(ObservationReport o, wrapper(reportList))
{
}
gives me the following compiler errors:
In file included from /usr/include/boost159/boost/foreach.hpp:71:0,
from test/src/MessageFillerTest.cpp:18:
/usr/include/boost159/boost/mpl/eval_if.hpp: In instantiation of ‘struct boost::mpl::eval_if<mpl_::bool_<true>, boost::range_const_iterator<wrapper, void>, boost::range_mutable_iterator<wrapper, void> >’:
/usr/include/boost159/boost/foreach.hpp:359:13: required from ‘struct boost::foreach_detail_::foreach_iterator<wrapper, mpl_::bool_<true> >’
/usr/include/boost159/boost/foreach.hpp:679:1: required by substitution of ‘template<class T> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, mpl_::bool_<true> >::type> boost::foreach_detail_::begin(
boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T, mpl_::bool_<true> >*, bool*) [with T = wrapper]’
test/src/MessageFillerTest.cpp:206:3: required from here
/usr/include/boost159/boost/mpl/eval_if.hpp:38:31: error: no type named ‘type’ in ‘boost::mpl::eval_if<mpl_::bool_<true>, boost::range_const_iterator<wrapper, void>, boost::range_mutable_iterator<wrapper, void> >::f_ {aka struct boost::ran
ge_const_iterator<wrapper, void>}’
typedef typename f_::type type;
^
In file included from test/src/MessageFillerTest.cpp:18:0:
test/src/MessageFillerTest.cpp: In member function ‘virtual void MessageFiller_XXX_Test::TestBody()’:
/usr/include/boost159/boost/foreach.hpp:1020:39: error: no matching function for call to ‘begin(const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<wrapper, mpl_::bool_<true> >*, bool*)’
, BOOST_FOREACH_SHOULD_COPY(COL))
^
/usr/include/boost159/boost/foreach.hpp:1101:77: note: in expansion of macro ‘BOOST_FOREACH_BEGIN’
if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else \
^
test/src/MessageFillerTest.cpp:206:3: note: in expansion of macro ‘BOOST_FOREACH’
BOOST_FOREACH(ObservationReport o, wrapper(reportList))
^
/usr/include/boost159/boost/foreach.hpp:660:1: note: candidate: template<class T, class C> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, C>::type> boost::foreach_detail_::begin(boost::foreach_detail_
::auto_any_t, boost::foreach_detail_::type2type<T, C>*, mpl_::true_*)
begin(auto_any_t col, type2type<T, C> *, boost::mpl::true_ *) // rvalue
^
/usr/include/boost159/boost/foreach.hpp:660:1: note: template argument deduction/substitution failed:
/usr/include/boost159/boost/foreach.hpp:964:46: note: cannot convert ‘boost::foreach_detail_::should_copy_impl(((mpl_::bool_<false>*)0u), ((mpl_::bool_<false>*)0u), (& _foreach_is_rvalue206))’ (type ‘bool*’) to type ‘mpl_::true_* {aka mp
l_::bool_<true>*}’
(boost::foreach_detail_::should_copy_impl( \
^
/usr/include/boost159/boost/foreach.hpp:1020:9: note: in expansion of macro ‘BOOST_FOREACH_SHOULD_COPY’
, BOOST_FOREACH_SHOULD_COPY(COL))
^
/usr/include/boost159/boost/foreach.hpp:1101:77: note: in expansion of macro ‘BOOST_FOREACH_BEGIN’
if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else \
^
test/src/MessageFillerTest.cpp:206:3: note: in expansion of macro ‘BOOST_FOREACH’
BOOST_FOREACH(ObservationReport o, wrapper(reportList))
^
/usr/include/boost159/boost/foreach.hpp:668:1: note: candidate: template<class T, class C> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, C>::type> boost::foreach_detail_::begin(boost::foreach_detail_
::auto_any_t, boost::foreach_detail_::type2type<T, C>*, mpl_::false_*)
begin(auto_any_t col, type2type<T, C> *, boost::mpl::false_ *) // lvalue
^
/usr/include/boost159/boost/foreach.hpp:668:1: note: template argument deduction/substitution failed:
/usr/include/boost159/boost/foreach.hpp:964:46: note: cannot convert ‘boost::foreach_detail_::should_copy_impl(((mpl_::bool_<false>*)0u), ((mpl_::bool_<false>*)0u), (& _foreach_is_rvalue206))’ (type ‘bool*’) to type ‘mpl_::false_* {aka m
pl_::bool_<false>*}’
(boost::foreach_detail_::should_copy_impl( \
^
/usr/include/boost159/boost/foreach.hpp:1020:9: note: in expansion of macro ‘BOOST_FOREACH_SHOULD_COPY’
, BOOST_FOREACH_SHOULD_COPY(COL))
^
/usr/include/boost159/boost/foreach.hpp:1101:77: note: in expansion of macro ‘BOOST_FOREACH_BEGIN’
if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else \
^
test/src/MessageFillerTest.cpp:206:3: required from here
/usr/include/boost159/boost/foreach.hpp:768:1: error: no type named ‘type’ in ‘struct boost::foreach_detail_::foreach_reference<wrapper, mpl_::bool_<true> >’
A wrapper can provide the required interface.
Note that in this example, operator[] of nasty_thing returns a reference to an int in an array of consecutive ints.
It this is not the case, we need to make the wrapper work in terms of an index.
#include <utility>
#include <memory>
//
// a nasty thing that contains consecutive ints
struct nasty_thing
{
int& operator[](int i);
int length();
};
// because we are dealing in consecutive ints, we can approximate an iterator with a pointer
struct wrapper
{
using iterator = int*;
wrapper(nasty_thing& n)
: begin_(std::addressof(n[0]))
, end_(begin_ + n.length())
{}
iterator begin() const { return begin_; }
iterator end() const { return end_; }
iterator begin_, end_;
};
extern nasty_thing& nt;
int main()
{
// simulate BOOST_FOREACH
for (auto& i : wrapper(nt))
{
}
}
This is the version in which nasty_thing does not represent contiguous memory:
#include <utility>
#include <memory>
#include <boost/foreach.hpp>
//
// a nasty thing that contains consecutive ints
struct nasty_thing
{
int& operator[](int i);
int length();
};
struct wrapper
{
struct iterator : std::iterator<std::forward_iterator_tag, int>
{
iterator(nasty_thing* p, int i) : pnt_(p), i_(i) {}
int& operator*() const {
return (*pnt_)[i_];
}
iterator& operator++() {
++i_;
return *this;
}
bool operator==(const iterator& r) const {
return i_ == r.i_;
}
bool operator!=(const iterator& r) const {
return i_ != r.i_;
}
nasty_thing* pnt_;
int i_;
};
// needed by BOOST_FOREACH
using const_iterator = iterator;
wrapper(nasty_thing& n)
: begin_{ std::addressof(n), 0 }
, end_{std::addressof(n), n.length()}
{}
iterator begin() const { return begin_; }
iterator end() const { return end_; }
iterator begin_, end_;
};
extern nasty_thing& nt;
int main()
{
// simulate BOOST_FOREACH
BOOST_FOREACH(auto& i, wrapper(nt))
{
}
}
There are docs:
http://www.boost.org/doc/libs/1_61_0/doc/html/foreach/extensibility.html
That walk you through extending the range concept to a custom container without modfying the container.
Create a range_begin and range_end free functions that take your container type in th container's namespace.
In namespace boost, specialize the traits class(es) that tells boost the iterator type:
template<> struct range_mutable_iterator< your::type::here > {
typedef your_iterator_type type;
};
template<> struct range_const_iterator< your::type::here > {
typedef your_iterator_type type;
};
And done.
This could probably be done with specializing boost::range_iterator<X>::type and boost::range_iterator<const X>::type and direct overloads of boost::begin/boost::end or ADL begin/end overloads, but I thought I'd use the documented extensibility path I found.
I will just post it for reference as Richard Hodges got it almost right.
struct wrapper
{
struct iterator : std::iterator<std::forward_iterator_tag, ObservationReport>
{
iterator(ObservationReportList* p, int i) : pnt_(p), i_(i) {}
ObservationReport& operator*() {
return (*pnt_)[i_];
}
iterator& operator++() {
++i_;
return *this;
}
bool operator==(const iterator& r) const {
return i_ == r.i_;
}
bool operator!=(const iterator& r) const {
return i_ != r.i_;
}
ObservationReportList* pnt_;
int i_;
};
typedef iterator const_iterator; // essential (in one way or another)
// http://www.boost.org/doc/libs/1_61_0/libs/range/doc/html/range/concepts/single_pass_range.html
wrapper(ObservationReportList & n)
: begin_( boost::addressof(n), 0 )
, end_( boost::addressof(n), n.length() )
{}
iterator begin() { return begin_; }
iterator end() { return end_; }
iterator begin() const { return begin_; } // essential for Single Pass concept
iterator end() const { return end_; } // essential for Single Pass concept
iterator begin_, end_;
};
// usage
ObservationReportList reportList;
// filling reportList
BOOST_FOREACH(ObservationReport o, wrapper(reportList))
{
}
So basically we missed const_iterator type definition (here only a typedef) and const versions of begin() and end()
I have an enum like this:
namespace api {
enum Operation {
INSERT =1,
UPDATE =2,
DELETE =3,
};
const Operation Operation_MIN = INSERT;
const Operation Operation_MAX = DELETE;
} // api
and I want to write some tests that use it:
#include "gtest.h"
class FWTest : public ::testing::Test {};
class FWTestPar : public FWTest, public ::testing::WithParamInterface<api::Operation>
{};
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
api::Operation_MIN, api::Operation_MAX+1)
);
To be able to increment enums, I wrote this operator:
template <typename E>
constexpr typename std::enable_if<std::is_enum<E>::value, E>::type
operator+(E val, int step)
{
return static_cast<E>(static_cast<int>(val) + step);
}
This compiles fine with G++ from 4.8 to 6.2, but clang refuses to find operator+ :
clang++ -g -std=c++14 -Wall -pedantic -Wextra -Wformat=2 -I ~/workspace/zarquon -o "enum_inc_real" "enum_inc_real.cc" (in directory: /tmp)
In file included from enum_inc_real.cc:1:
third_party/gmock/gtest/gtest.h:10250:34: error: assigning to 'api::Operation' from incompatible type 'int'
for (T i = begin; i < end; i = i + step)
^ ~~~~~~~~
third_party/gmock/gtest/gtest.h:10191:33: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::CalculateEndIndex' requested here
step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
^
third_party/gmock/gtest/gtest.h:15815:11: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::RangeGenerator' requested here
new internal::RangeGenerator<T, IncrementT>(start, end, step));
^
third_party/gmock/gtest/gtest.h:15820:10: note: in instantiation of function template specialization 'testing::Range<api::Operation, int>' requested here
return Range(start, end, 1);
^
enum_inc_real.cc:34:55: note: in instantiation of function template specialization 'testing::Range<api::Operation>' requested here
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
^
In file included from enum_inc_real.cc:1:
third_party/gmock/gtest/gtest.h:10213:14: error: assigning to 'api::Operation' from incompatible type 'int'
value_ = value_ + step_;
^ ~~~~~~~~~~~~~~
third_party/gmock/gtest/gtest.h:10204:5: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Iterator::Advance' requested here
Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
^
third_party/gmock/gtest/gtest.h:10195:16: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Iterator::Iterator' requested here
return new Iterator(this, begin_, 0, step_);
^
third_party/gmock/gtest/gtest.h:10189:3: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::Begin' requested here
RangeGenerator(T begin, T end, IncrementT step)
^
third_party/gmock/gtest/gtest.h:15815:11: note: in instantiation of member function 'testing::internal::RangeGenerator<api::Operation, int>::RangeGenerator' requested here
new internal::RangeGenerator<T, IncrementT>(start, end, step));
^
third_party/gmock/gtest/gtest.h:15820:10: note: in instantiation of function template specialization 'testing::Range<api::Operation, int>' requested here
return Range(start, end, 1);
^
enum_inc_real.cc:34:55: note: in instantiation of function template specialization 'testing::Range<api::Operation>' requested here
INSTANTIATE_TEST_CASE_P(Piping, FWTestPar, ::testing::Range(
^
If the templated operator+ is defined inside the api namespace, then clang++ also accepts this code.
So, which compiler is right?
Do I really have to drag operator+ into every single namespace that contains an enum I want to increment?
The odd thing is that gtest does something similar to this:
namespace testin_ {
namespace intl {
template<typename T>
class PII {};
template<typename T, typename Inc>
class RG{
public:
RG(T begin, T end, Inc s)
: begin_{begin}, end_{end}, step_{s}, end_index_{CalculateEndIndex(begin, end, s)}
{}
T begin() { return begin_; }
T end () { return end_; }
private:
class It : public PII<T> {
void advance() {
value_ = value_ + step_;
}
T value_;
int index_;
Inc step_;
};
static int CalculateEndIndex(const T& begin,
const T& end,
const Inc& step) {
int end_index = 0;
for (T i = begin; i < end; i = i + step)
end_index++;
return end_index;
}
const T begin_;
const T end_;
const Inc step_;
const int end_index_;
};
} // intl
template<typename T, typename Inc>
intl::RG<T, Inc> Rg(T start, T end, Inc step) {
return intl::RG<T, Inc>(start, end, step);
}
}
auto gen = testin_::Rg(api::INSERT, api::DELETE, 1);
bu that is accepted without complaints by clang++