Custom iterator in range based for: issue with constness - c++

The following code is based on that found in Modern C++ programming cookbook, and is compiled in VS 2017:
#include <iostream>
using namespace std;
template <typename T, size_t const Size>
class dummy_array
{
T data[Size] = {};
public:
T const & GetAt(size_t const index) const
{
if (index < Size) return data[index];
throw std::out_of_range("index out of range");
}
// I have added this
T & GetAt(size_t const index)
{
if (index < Size) return data[index];
throw std::out_of_range("index out of range");
}
void SetAt(size_t const index, T const & value)
{
if (index < Size) data[index] = value;
else throw std::out_of_range("index out of range");
}
size_t GetSize() const { return Size; }
};
template <typename T, typename C, size_t const Size>
class dummy_array_iterator_type
{
public:
dummy_array_iterator_type(C& collection,
size_t const index) :
index(index), collection(collection)
{ }
bool operator!= (dummy_array_iterator_type const & other) const
{
return index != other.index;
}
T const & operator* () const
{
return collection.GetAt(index);
}
// I have added this
T & operator* ()
{
return collection.GetAt(index);
}
dummy_array_iterator_type const & operator++ ()
{
++index;
return *this;
}
private:
size_t index;
C& collection;
};
template <typename T, size_t const Size>
using dummy_array_iterator = dummy_array_iterator_type<T, dummy_array<T, Size>, Size>;
// I have added the const in 'const dummy_array_iterator_type'
template <typename T, size_t const Size>
using dummy_array_const_iterator = const dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;
template <typename T, size_t const Size>
inline dummy_array_iterator<T, Size> begin(dummy_array<T, Size>& collection)
{
return dummy_array_iterator<T, Size>(collection, 0);
}
template <typename T, size_t const Size>
inline dummy_array_iterator<T, Size> end(dummy_array<T, Size>& collection)
{
return dummy_array_iterator<T, Size>(collection, collection.GetSize());
}
template <typename T, size_t const Size>
inline dummy_array_const_iterator<T, Size> begin(dummy_array<T, Size> const & collection)
{
return dummy_array_const_iterator<T, Size>(collection, 0);
}
template <typename T, size_t const Size>
inline dummy_array_const_iterator<T, Size> end(dummy_array<T, Size> const & collection)
{
return dummy_array_const_iterator<T, Size>(collection, collection.GetSize());
}
int main(int nArgc, char** argv)
{
dummy_array<int, 10> arr;
for (auto&& e : arr)
{
std::cout << e << std::endl;
e = 100; // PROBLEM
}
const dummy_array<int, 10> arr2;
for (auto&& e : arr2) // ERROR HERE
{
std::cout << e << std::endl;
}
}
Now, the error is pointing at the line
T & operator* ()
stating
'return': cannot convert from 'const T' to 'T &'"
...which is raised from my range based for loop on arr2.
Why is the compiler choosing the none-constant version of operator*()?. I have looked at this for a long time; I think its because it thinks that the object on which it is calling this operator is not constant: this should be a dummy_array_const_iterator. But, this object has been declared to be constant via
template <typename T, size_t const Size>
using dummy_array_const_iterator = const dummy_array_iterator_type<T, dummy_array<T, Size> const, Size>;
...so I really don't understand what is happening. Can someone please clarify?
TIA

dummy_array_const_iterator::operator * should always return T const & regardless of constness of the iterator object itself.
The easiest way to achieve this is probably just to declare it with T const as underlying iterator value type:
template <typename T, size_t const Size>
using dummy_array_const_iterator = dummy_array_iterator_type<T const, dummy_array<T, Size> const, Size>;
Since you are returning the iterator by value its constness can be easily lost by c++ type deduction rules and just declaring dummy_array_const_iterator as alias to const dummy_array_iterator_type is not enough. i.e. the following fails:
#include <type_traits>
struct I { };
using C = I const;
C begin();
int bar()
{
auto x = begin(); // type of x is deduced as I
static_assert(std::is_same<I, decltype(x)>::value, "same"); // PASS
static_assert(std::is_same<decltype(begin()), decltype(x)>::value, "same"); // ERROR
}

I found a way to enable T& operator*() only when C is not constant:
template <class Tp = T>
typename std::enable_if<std::is_const<C>::value, Tp>::type const& operator* () const
{
return collection.GetAt(index);
}
template <class Tp = T>
typename std::enable_if<!std::is_const<C>::value, Tp>::type & operator* () const
{
return collection.GetAt(index);
}
I have no idea about the syntax (which I get from https://stackoverflow.com/a/26678178)

Related

c++ Force implicit conversion on pass as argument

I have problem with implicit conversions in C++.
I'm trying to create some Expression template for vector arithmetics (I know that same libraries already exists. I'm just learning C++ so I wanted to try something with templates).
I would like to create class Vector, that is able to compute like this:
simd::test::Vector<char, 5> a;
simd::test::Vector<short, 5> b;
auto ret = a + b + a + b;
, where on output would be Vector of shorts becouse short is bigger type than char.
Right now, I have class that is able to adds vectors of same data types. For different types I have to call explicit conversion:
//simd::test::Vector<short, 5>(a)
auto ret = simd::test::Vector<short, 5>(a) + b + simd::test::Vector<short, 5>(a) + b;
Is possible to implicit convert Vector before pass into function "operator+()"? Here is my code of Vector:
#pragma once
#include <type_traits>
namespace simd {
namespace test {
template<typename R, std::size_t Dim,
typename std::enable_if<std::is_arithmetic<R>::value>::type* = nullptr
>
class Vector_expression {
public:
static constexpr std::size_t size = Dim;
virtual const R operator[] (std::size_t index) const = 0;
virtual ~Vector_expression() = default;
};
template<typename T, std::size_t Dim>
class Vector final : public Vector_expression<T, Dim> {
private:
T data[Dim];
public:
Vector() = default;
template<typename R>
Vector(const Vector_expression<R, Dim> &obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
}
template<typename R>
Vector(Vector_expression<R, Dim> &&obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
}
template<typename R>
Vector<T, Dim> & operator=(const Vector_expression<R, Dim> &obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
return (*this);
}
template<typename R>
Vector<T, Dim> & operator=(Vector_expression<R, Dim> && obj) {
for(std::size_t index = 0; index < Dim; ++index) {
data[index] = obj[index];
}
return (*this);
}
virtual const T operator[] (std::size_t index) const override {
return data[index];
}
T & operator[] (std::size_t index) {
return data[index];
}
virtual ~Vector() = default;
};
template<typename E1, typename E2, typename R, std::size_t Dim>
class Vector_sum final : public Vector_expression<R, Dim> {
private:
const E1 & _lhs;
const E2 & _rhs;
public:
Vector_sum() = delete;
Vector_sum(const E1 & lhs, const E2 & rhs) :
_lhs(lhs),
_rhs(rhs)
{}
virtual const R operator[] (std::size_t index) const override {
return _lhs[index] + _rhs[index];
}
virtual ~Vector_sum() = default;
};
template<typename R, std::size_t Dim>
Vector_sum<Vector_expression<R, Dim>, Vector_expression<R, Dim>, R, Dim> operator+ (const Vector_expression<R, Dim> & lhs, const Vector_expression<R, Dim> & rhs) {
return {lhs, rhs};
}
}
}
Just define an operator+ that allows different argument types. The one catch is determining the element type of the resulting sum. Probably the best option is to use whatever the result of adding two elements is. One way to write this type is:
decltype(std::declval<const R1>() + std::declval<const R2>())
Or if you know the types are built-in arithmetic types, that would be the same as
std::common_type_t<R1, R2>
Or using a trailing return type, we can take advantage of the function parameters to shorten the std::declval expressions:
template<typename R1, typename R2, std::size_t Dim>
auto operator+ (const Vector_expression<R1, Dim> & lhs,
const Vector_expression<R2, Dim> & rhs)
-> Vector_sum<Vector_expression<R1, Dim>, Vector_expression<R2, Dim>,
decltype(lhs[0] + rhs[0]), Dim>
{
return {lhs, rhs};
}
It could be done using templates and std::common_type, something like this:
template<typename T1, typename T2, size_t S>
simd::test::Vector<typename std::common_type<T1, T2>::type, S>
operator+(simd::test::Vector<T1, S> const& v1,
simd::test::Vector<T2, S> const& v2)
{
// TODO: Implementation...
}

Constexpr Iterator in C++

I am interested in creating a very minimal constexpr container for a personal project. The most important thing I need is a container with true constexpr iterators. They are going to be added to the standard eventually (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0858r0.html) but I would like to know how to implement them in current C++.
Let's say I had a data structure like this:
template <typename T, unsigned N>
struct array {
const T data[N];
template<typename... Args>
constexpr array(const Args&... args) : data{args...} {
}
struct array_iterator {
T const* ptr;
constexpr array_iterator(const T* ptr) : ptr(ptr) {}
constexpr void operator++() { ++ptr; }
constexpr void operator--() { --ptr; }
constexpr T const& operator* () const { return *ptr; }
constexpr bool operator==(const array_iterator& rhs) const { return *(*this) == *rhs; }
constexpr bool operator!=(const array_iterator& rhs) const { return !(*this == rhs); }
};
constexpr array_iterator begin() const { return array_iterator(data); }
constexpr array_iterator end() const { return array_iterator(data + N); }
};
What I need is to be able to actually use the iterators in a constexpr context:
constexpr array<int, 3> arr(1, 2, 3);
constexpr auto it = arr.begin();
But obviously, since I am messing around with the non-constexpr ptr sub-object, I encounter errors like so:
iterator.cpp:46:18: error: constexpr variable 'it' must be initialized by a
constant expression
constexpr auto it = arr.begin();
^ ~~~~~~~~~~~
iterator.cpp:46:18: note: pointer to subobject of 'arr' is not a constant
expression
iterator.cpp:45:27: note: declared here
constexpr array<int, 3> arr(1, 2, 3);
^
So what would be a minimal constexpr iterator for a container like array?
constexpr has a few meanings.
It can mean a value which can always be calculated at compile time. It can mean a function that, given compile time arguments, can generate output that can be calculated at compile time.
You have found that your iterators cannot refer to an automatic storage constexpr object. On the other hand, they can be invoked within a constexpr function.
template <typename T, unsigned N>
struct array {
const T data[N];
template<typename... Args>
constexpr array(const Args&... args) : data{args...} {}
struct array_iterator {
T const* ptr;
constexpr array_iterator(const T* ptr) : ptr(ptr) {}
constexpr void operator++() { ++ptr; }
constexpr void operator--() { --ptr; }
constexpr T const& operator* () const { return *ptr; }
constexpr bool operator==(const array_iterator& rhs) const { return ptr == rhs.ptr; }
constexpr bool operator!=(const array_iterator& rhs) const { return !(*this == rhs); }
};
constexpr array_iterator begin() const { return array_iterator(data); }
constexpr array_iterator end() const { return array_iterator(data + N); }
};
constexpr int sum() {
int retval = 0;
constexpr array<int, 3> arr = {1,2,3};
for (int x : arr)
retval += x;
return retval;
}
int main() {
array<int, sum()> test;
(void)test;
}
your operator== was broken but after fixing it we can call sum() in a constexpr context, which in turn uses iterators.

c++: template container, operator[] not working as expected

I'm trying to implement a template class that wraps around a
STL container of pointers, as in:
tContainer_t<T, vector<T*> >
or
tContainer_t<T, list<T*> >
Here's the class declaration:
template <typename T, typename Container>
class tContainer_t
{
public:
tContainer_t() {} //Default CTOR
~tContainer_t() {} //DTOR
bool IsEmpty() const;
size_t Size() const;
bool Insert(T* _element);
T* Find(T _value);
T* Remove(T _value);
T* operator[](size_t _index);
private:
tContainer_t(const tContainer_t& _other); //Disable Copy
Container m_container;
};
I want to practice different implementations for operator[] (for vector and list),so I wrote:
template <typename T, typename Container>
T* tContainer_t<T, Container>::operator[](size_t _index)
{
T* retval;
if (_index > m_container.size())
{
return 0;
}
if (typeid(m_container) == typeid(vector<T*>))
{
retval = m_container[_index];
}
if (typeid(m_container) == typeid(list<T*>))
{
typename Container::iterator contIter = m_container.begin();
advance(contIter, _index);
retval = *contIter;
}
return retval;
}
and I get strange behavior. when I use a vector in main, the code works fine. When I use a list, it doesn't even compile, showing:
container.h: In instantiation of ‘T* tContainer_t<T, Container>::operator[](size_t) [with T = int; Container = std::list<int*>; size_t = long unsigned int]’:
container.cpp:10:14: required from here
container.h:141:23: error: no match for ‘operator[]’ (operand types are ‘std::list<int*>’ and ‘size_t {aka long unsigned int}’)
retval = m_container[_index];
Here's how I'm using it:
int main(int argc, char const *argv[])
{
tContainer_t<int, list<int*> > a;
int i = 5;
a.Insert(&i);
cout << *a[0] << endl;
return 0;
}
All the code in a function will get compiled, regardless of whether or not it'll ever get run. So this:
if (typeid(m_container) == typeid(vector<T*>))
{
retval = m_container[_index];
}
cannot possibly ever work for list<T>, since list has no operator[]. Hence the compiler error. It doesn't matter that it won't get run, it still has to compile. The way to do this instead is to dispatch to different functions based on the type of m_container:
template <typename T, typename Container>
T* tContainer_t<T, Container>::operator[](size_t _index)
{
if (_index > m_container.size())
{
return 0;
}
return _get_index(m_container, _index);
}
With a vector version:
template <class T>
T* tContainer_t<T, Container>::_get_index(std::vector<T*>& v, size_t _index) {
return v[_index];
}
And a list version:
template <class T>
T* tContainer_t<T, Container>::_get_index(std::list<T*>& lst, size_t _index)
{
typename Container::iterator contIter = lst.begin();
advance(contIter, _index);
return *contIter;
}
At least that's a good general approach. In this case, we don't actually have to split them up, since we can use advance() for both container types. It'll be cheap for the vector and expensive for list:
template <typename T, typename Container>
T* tContainer_t<T, Container>::operator[](size_t _index)
{
if (_index >= m_container.size()) // note the off-by-one error I fixed here
{
return nullptr;
}
auto it = m_container.begin();
std::advance(it, _index);
return *it;
}
Or simply:
return *std::next(m_container.begin(), _index);

Autoconverting template< T> to template<const T>

This piece below is supposed to be primarily for a string view with T={char, const char} being the primary intended template instantiation target.
The cmp function is supposed to compare the views analogously to strcmp.
The problem is that while char* happily converts to const char* I don't know how to get SVec<char> to convert to SVec<const char> just as happily.
The last line (cout<<(cmp(rv, rvc));) won't compile. I have to do the convertion explicitly (cmp(SVec<const char>(rv), rvc)). Can it be automatic like with char* to const char*?
The code (much simplified):
template <typename T>
class SVec {
protected:
T* begin_;
size_t size_;
public:
SVec(T* begin, size_t size) : begin_(begin), size_(size) {};
SVec(T* begin, T* end) : begin_(begin), size_(end-begin) {};
SVec(T* begin) : begin_(begin) { while (*(begin++)) {}; size_ = begin - 1 - begin_; }
//^null element indicates the end
///Conversion
operator SVec<const T>() const { return SVec<const T>(begin_, size_); }
};
//General lexicographic compare
template <typename T>
inline int cmp(const SVec<const T>& l, const SVec<const T> & r){
return 1;
}
//Char specialization
template <> inline int cmp<char>(const SVec<const char>& l, const SVec<const char>& r){
return 1;
}
//Explicit instantiation
template int cmp<char>(const SVec<const char>& l, const SVec<const char>& r);
#include <iostream>
int main(){
using namespace std;
char ar[] = "st";
SVec<char> sv = ar;
SVec<const char> svc = "str";
cout<<(cmp(SVec<const char>(sv), svc));
cout<<(cmp(sv, svc));
}
So the first thing you should probably do is make cmp a Koenig operator.
Then we can tag dispatch between the char and non-char versions:
template <typename T>
class SVec {
private:
static T* find_end(T* in) {
// I think while(*in)++in; would be better
// then the end is the null, not one-past-the-null.
while(*in++) {};
return in;
}
protected:
T* begin_ = nullptr;
size_t size_ = 0;
public:
SVec() = default;
SVec(SVec const&) = default;
SVec(T* begin, size_t size) : begin_(begin), size_(size) {};
SVec(T* begin, T* end) : SVec(begin, end-begin) {}
SVec(T* begin) : SVec(begin, find_end(begin)) {}
operator SVec<const T>() const { return SVec<const T>(begin_, size_); }
friend int cmp(SVec<T> l, SVec<T> r) {
return cmp_impl(l, r, std::is_same<std::decay_t<T>,char>{});
}
private:
static int cmp_impl(SVec<const char> l, SVec<const char> r, std::true_type){
return 1;
}
static int cmp_impl(SVec<const T> l, SVec<const T> r, std::false_type){
return 1;
}
};
std::decay_t and enable_if_t are C++14, but are just short versions of the typename spam _t-less versions.
Notice I take things by value instead of const& : a pointer and a size_t do not merit passing by reference.
I also forward all ctors into 2 bottlenecks.
...
The Koenig operator friend int cmp uses ADL to be found. It is not a template function, but rather a function that is generated for each template class instance, which is an important distinction.
Koenig operators avoid the problems of template operators, while allowing them to vary with the type of the template. Such an operator can only be found via ADL (argument dependent lookup).
It then dispatches to the _impl overloads (which are now const-correct) based on if T is a char or not at compile time.

Constexpr find implementation

After answering this question and reading this talk and looking at this code, I want to implement constexpr find with just simple array class.
Consider following example:
#include <cstddef>
template <class It, class T>
constexpr auto constexpr_find(const It& b, const It& e, T value) {
auto begin = b;
while (begin != e) {
if (*begin == value) break;
++begin;
}
return *begin;
}
template<typename T, size_t N>
class array
{
public:
typedef T* iterator;
typedef const T* const_iterator;
constexpr auto begin() const { return const_iterator(array_); }
constexpr auto end() const { return const_iterator(array_ + N); }
T array_[N];
static constexpr size_t size = N;
};
int main()
{
constexpr array<int, 3> array{{0,2,3}};
static_assert(constexpr_find(array.begin(), array.end(), 0) == 0, "");
}
compiles as expected
And with custom constexpr iterator:
template<class T>
class array_iterator
{
public:
constexpr array_iterator(const T* v) : iterator(v)
{
}
constexpr const T& operator * () const { return *iterator; }
constexpr array_iterator& operator ++()
{
++iterator;
return *this;
}
constexpr bool operator != (const array_iterator& other) const { return iterator != other.iterator; }
private:
const T* iterator;
};
In array class:
typedef const array_iterator<const T> const_iterator;
that's the only difference, compiler give me error:
in constexpr expansion of
constexpr_find<array_iterator<const int>, int>(array.array<T,
N>::begin<int, 3u>(), array.array<T, N>::end<int, 3u>(), 0)
error: (((const int*)(& array.array<int, 3u>::array_)) != (((const
int*)(& array.array<int, 3u>::array_)) + 12u)) is not a constant
expression
Live example
Is this gcc bug, since clang compile this fine, or there is difference in two snippets?
I cannot say for sure, but you store pointers for array's member into external iterator class, it may be the reason for that error.
--------- update start ---------
Here is the minimal snippet that demonstrates the problem:
constexpr const struct A { int i[2]; } a {{0,0}};
int main ()
{
static_assert (nullptr != a.i , ""); // ok
static_assert (nullptr != a.i+0, ""); // ok
static_assert (nullptr != a.i+1, ""); // error
}
It seems to be forbidden to have pointers to array elements (with non-zero offset) in constant expressions.
--------- update end ---------
The workaround is trivial - store the pointer to array object and offset.
Live
#include <cstddef>
template <class It, class T>
constexpr auto constexpr_find(const It& b, const It& e, T value) {
auto begin = b, end = e;
while (begin != end) {
if (*begin == value) break;
++begin;
}
return *begin;
}
template<class Array>
class array_iterator
{
public:
constexpr array_iterator(const Array& a, size_t pos=0u) : array_(&a), pos_ (pos)
{
}
constexpr const typename Array::value_type&
operator * () const { return (*array_)[pos_]; }
constexpr array_iterator& operator ++()
{
++pos_;
return *this;
}
constexpr bool operator != (const array_iterator& other) const
{ return array_ != other.array_ || pos_ != other.pos_; }
private:
const Array* array_;
size_t pos_;
};
template<typename T, size_t N>
class array
{
public:
typedef T value_type;
typedef const array_iterator<array> const_iterator;
constexpr T const& operator[] (size_t idx) const { return array_[idx]; }
constexpr auto begin() const { return const_iterator(*this); }
constexpr auto end() const { return const_iterator(*this, N); }
T array_[N];
static constexpr size_t size = N;
};
int main()
{
constexpr array<int, 3> array{{0,2,3}};
static_assert(constexpr_find(array.begin(), array.end(), 0) == 0, "");
}
By the way, it is possible to implement C++11 version of constexpr enabled find:
Live
#include <cstddef>
#include <cassert>
#if !defined(__clang__) && __GNUC__ < 5
// TODO: constexpr asserts does not work in gcc4, but we may use
// "thow" workaround from
// http://ericniebler.com/2014/09/27/assert-and-constexpr-in-cxx11/
# define ce_assert(x) ((void)0)
#else
# define ce_assert(x) assert(x)
#endif
namespace my {
template <class It, class T>
inline constexpr It
find (It begin, It end, T const& value) noexcept
{
return ! (begin != end && *begin != value)
? begin
: find (begin+1, end, value);
}
template<class Array>
class array_iterator
{
public:
using value_type = typename Array::value_type;
constexpr array_iterator(const Array& array, size_t size = 0u) noexcept
: array_ (&array)
, pos_ (size)
{}
constexpr const value_type operator* () const noexcept
{
return ce_assert (pos_ < Array::size), (*array_) [pos_];
}
#if __cplusplus >= 201402L // C++14
constexpr
#endif
array_iterator& operator ++() noexcept
{
return ce_assert (pos_ < Array::size), ++pos_, *this;
}
constexpr array_iterator operator+ (size_t n) const noexcept
{
return ce_assert (pos_+n <= Array::size), array_iterator (*array_, pos_+n);
}
friend constexpr bool
operator != (const array_iterator& i1, const array_iterator& i2) noexcept
{
return i1.array_ != i2.array_ || i1.pos_ != i2.pos_;
}
friend constexpr size_t
operator- (array_iterator const& i1, array_iterator const& i2) noexcept
{
return ce_assert (i1.array_ == i2.array_), i1.pos_ - i2.pos_;
}
private:
const Array* array_;
size_t pos_;
};
template<typename T, size_t N>
class array
{
public:
using value_type = T;
using const_iterator = const array_iterator<array>;
constexpr value_type const&
operator[] (size_t idx) const noexcept
{ return array_[idx]; }
constexpr const_iterator begin() const noexcept
{ return const_iterator(*this); }
constexpr const_iterator end() const noexcept
{ return const_iterator(*this, N); }
T array_[N];
static constexpr size_t size = N;
};
}
int main()
{
static constexpr my::array<int, 3> array{{0,2,3}};
static_assert (
find (array.begin(), array.end(), 2) - array.begin () == 1,
"error");
}
You may also be interested to check Sprout library, it contains a lot of constexpr data structures and algorithms.