Constexpr find implementation - c++

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.

Related

Why Cant I Substitute a String into a NTTP

Take the following example:
template<const auto& A>
class B {};
int main() {
B<"abc"> b;
return 0;
}
I have tried several variants of this, I dont understand why it does not work, it works for this library, it supports the following
constexpr auto match(std::string_view sv) noexcept {
return ctre::match<"h.*">(sv);
}
and it is receiving a const auto& specific context type alias, place where the type is forwarded where it ends up
It also does not really work if I do the following
template<const auto& StringParameterConstant, size_t IndexParameterConstant = 0>
consteval size_t string_length()
{
if constexpr(StringParameterConstant[IndexParameterConstant] != '\0')
return string_length<StringParameterConstant, IndexParameterConstant + 1>();
else
return IndexParameterConstant;
}
template<char... CharacterParameterConstants>
struct TemplateString {
constexpr static const size_t length = sizeof...(CharacterParameterConstants);
constexpr static const char array[length] = { CharacterParameterConstants... };
};
template<
const auto& StringParameterConstant,
size_t IndexParameterConstant,
size_t LengthParameterConstant,
char... CharacterParameterConstants
>
consteval auto to_template_string()
{
//...
}
template<const auto& StringParameterConstant>
using TemplateStringType = decltype(to_template_string //...
template<
const auto& PatternParameterConstant,
const auto& NameParameterConstant, //...
>
struct RegexTerm
{
using PatternType = TemplateStringType<PatternParameterConstant>;
using NameType = TemplateStringType<NameParameterConstant>;
constexpr static ctpg::regex_term<PatternType::string> term( // OK
NameType::string, // OK
//...
);
};
//...
/*tried with constexpr static and in a consteval/constexpr function*/
RegexTerm<"[1-9][0-9]*", "Numerical">::term; // Error
You will need some kind of string wrapper.
A char const* won't really template over the value of the string, but it will template over the pointer to that character array. This is not what you want.
The solution is to implement some kind of fixed_string class:
template<std::size_t n>
struct fixed_string {
constexpr fixed_string() = default;
constexpr fixed_string(const char(&str)[n + 1]) noexcept {
auto i = std::size_t{0};
for (char const c : str) {
_data[i++] = c;
}
_data[n] = static_cast<Char>(0);
}
friend constexpr auto operator<=>(fixed_string const&, fixed_string const&) = default;
friend constexpr auto operator==(fixed_string const&, fixed_string const&) -> bool = default;
[[nodiscard]]
static constexpr auto size() noexcept -> std::size_t {
return n;
}
[[nodiscard]]
static constexpr auto empty() noexcept -> bool {
return n == 0;
}
constexpr auto data() const& noexcept -> char const* {
return _data;
}
constexpr auto data() & noexcept -> char* {
return _data;
}
constexpr auto begin() const& noexcept -> char const* {
return _data;
}
constexpr auto end() const& noexcept -> char const* {
return _data + n;
}
constexpr auto begin() & noexcept -> char* {
return _data;
}
constexpr auto end() & noexcept -> char* {
return _data + n;
}
constexpr auto operator[](std::size_t index) noexcept {
return _data[index];
}
char _data[n + 1];
};
// template argument deduction
template<std::size_t n>
fixed_string(char const(&)[n]) -> fixed_string<n - 1>;
Then, you can then use that fixed string inside template arguments:
template<fixed_string str> // use class template argument deduction
class B {};
int main() {
B<"abc"> b; // actually works!
return 0;
}
Note that you will need C++20 enabled to use that feature.

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.

How to make custom keyword statement

How would I go about making a function that uses braces like if/for/while statements? I'm referring to this as a 'keyword statement' because I don't know what else to call it.
Meaning, for example, if I wanted to make a 'repeat' function:
repeat(3)
{
//do something
}
I guess a better question is, is this possible? If so, how would one go about doing this?
Don't do that [#define repeat] - don't try to change the syntax of the programming language you're using. That will make your code far less readable for anyone else.
You might define a range similar to a python range:
// Range
// =====
#include <iterator>
#include <utility>
template<typename T>
class Range
{
public:
typedef T value_type;
public:
class iterator
{
public:
typedef typename std::forward_iterator_tag iterator_category;
typedef typename std::size_t size_type;
typedef typename std::ptrdiff_t difference_type;
typedef T value_type;
typedef const T& reference;
typedef const T* pointer;
public:
iterator(const T& value) noexcept
: m_value(value)
{}
reference operator * () const noexcept { return m_value; }
pointer operator -> () const noexcept { return &m_value; }
iterator& operator ++ () noexcept { ++m_value; return *this; }
friend bool operator == (const iterator & a, const iterator b) noexcept {
return a.m_value == b.m_value;
}
friend bool operator != (const iterator & a, const iterator b) noexcept {
return a.m_value != b.m_value;
}
private:
T m_value;
};
public:
Range(const T& first, const T& last) noexcept
: m_first(first), m_last(last)
{}
Range(T&& first, T&& last) noexcept
: m_first(std::move(first)), m_last(std::move(last))
{}
Range(Range&& other) noexcept
: m_first(std::move(other.m_first)),
m_last(std::move(other.m_last))
{}
Range& operator = (Range&& other) noexcept {
m_first = std::move(other.m_first);
m_last = std::move(other.m_last);
return *this;
}
iterator begin() const noexcept { return m_first; }
iterator end() const noexcept { return m_last; }
private:
T m_first;
T m_last;
};
template<typename T>
inline Range<T> range(T&& first, T&& last) noexcept {
return Range<T>(std::move(first), std::move(last));
}
// Test
// ====
#include <iostream>
int main() {
for(auto i : range(0, 3))
std::cout << i << '\n';
}
A more sophisticated implementation would consider containers and iterators, too.
You could define a macro taking 1 argument:
#define repeat(COUNT) \
for (unsigned int i = 0; i < (COUNT); ++i)
and leave the brakets empty after it, the preprocessor will expand the following example:
repeat(3)
{
//do something
}
into:
for (unsigned int i = 0; i < (3); ++i)
{
//do something
}
Demo

STL way to access more elements at the same time in a loop over a container

Is it possible to rewrite this raw loop:
vector<double> v { ... };
for (size_t i = 1; i<v.size(); ++i) {
v[i]*=v[i-1];
}
or the even more cryptic:
for (auto i = v.begin()+1; i<v.end(); ++i) {
(*i) *= *(i-1);
}
(and similar, maybe accessing also v[i-2], ...) in a more STLish way?
Are there other forms which are equal or better (both in style and performances) than the ones above?
The most STLish way I can imagine:
std::partial_sum(std::begin(v), std::end(v),
std::begin(v), std::multiplies<double>());
Example:
#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>
#include <functional>
int main()
{
std::vector<double> v{ 1.0, 2.0, 3.0, 4.0 };
std::partial_sum(std::begin(v), std::end(v),
std::begin(v), std::multiplies<double>());
std::copy(std::begin(v), std::end(v),
std::ostream_iterator<double>(std::cout, " "));
}
Output:
1 2 6 24
Live demo link.
You can do that with std::transform, the overload that takes two input sequences:
int container[] = {1,2,3};
std::transform(
std::begin(container), std::end(container) - 1,
std::begin(container) + 1, std::begin(container) + 1,
[](auto a, auto b) { return a * b; }
);
But the hand-coded loop is much more readable.
If you want a generic way to do sliding windows rather than a non-transferable STL-ish way to answer your particular problem, you could consider the following ridiculous nonsense:
#include <array>
#include <cstddef>
#include <memory>
#include <tuple>
namespace detail {
template<std::size_t, typename>
class slide_iterator;
}
template<std::size_t N, typename I>
detail::slide_iterator<N, I> slide_begin(const I&);
template<std::size_t N, typename I>
detail::slide_iterator<N, I> slide_end(const I&);
namespace detail {
template<std::size_t N, typename T, typename... Args>
struct repeat {
typedef typename repeat<N - 1, T, T, Args...>::type type;
template<typename I>
type operator()(const I& it, Args&... args) const {
auto jt = it;
return repeat<N - 1, T, T, Args...>()(++jt, args..., *it);
}
};
template<typename T, typename... Args>
struct repeat<0, T, Args...> {
typedef std::tuple<Args&...> type;
template<typename I>
type operator()(const I&, Args&... args) const {
return type(args...);
}
};
template<std::size_t N, typename I /* forward iterator */>
class slide_iterator {
public:
typedef slide_iterator iterator;
typedef decltype(*I{}) reference;
typedef typename repeat<N, reference>::type window_tuple;
slide_iterator() = default;
~slide_iterator() = default;
slide_iterator(const iterator& it) = default;
iterator& operator=(const iterator& it) = default;
window_tuple operator*() const {
return repeat<N, reference>()(first_);
}
iterator& operator++() { // prefix
++first_;
++last_;
return *this;
}
iterator operator++(int) { // postfix
auto tmp{*this};
operator++();
return tmp;
}
friend void swap(iterator& lhs, iterator& rhs) {
swap(lhs.first_, rhs.first_);
swap(lhs.last_, rhs.last_);
swap(lhs.dirty_, rhs.dirty_);
swap(lhs.window_, rhs.window_);
}
friend bool operator==(const iterator& lhs, const iterator& rhs) {
return lhs.last_ == rhs.last_;
}
friend bool operator!=(const iterator& lhs, const iterator& rhs) {
return !operator==(lhs, rhs);
}
friend iterator slide_begin<N, I>(const I& it);
friend iterator slide_end<N, I>(const I& it);
private:
I first_;
I last_; // for equality only
};
template<typename T, std::size_t N>
struct slide_helper {
T& t;
auto begin() -> decltype(slide_begin<N>(t.begin())) {
return slide_begin<N>(t.begin());
}
auto end() -> decltype(slide_end<N>(t.end())) {
return slide_end<N>(t.end());
}
};
} // ::detail
// note it is undefined to call slide_begin<N>() on an iterator which cannot
// be incremented at least N - 1 times
template<std::size_t N, typename I>
detail::slide_iterator<N, I> slide_begin(const I& it) {
detail::slide_iterator<N, I> r;
r.first_ = r.last_ = it;
std::advance(r.last_, N - 1);
return r;
}
template<std::size_t N, typename I>
detail::slide_iterator<N, I> slide_end(const I& it) {
detail::slide_iterator<N, I> r;
r.last_ = it;
return r;
}
template<std::size_t N, typename T>
detail::slide_helper<T, N> slide(T& t) {
return {t};
}
Example usage:
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{1, 2, 3, 4};
/* helper for
for (auto it = slide_begin<2>(v.begin()),
et = slide_end<2>(v.end()); it != et ... BLAH BLAH BLAH */
for (const auto& t : slide<2>(v)) {
std::get<1>(t) *= std::get<0>(t);
}
for (const auto& i : v) {
std::cout << i << std::endl;
}
}
This is an implementation that keeps an array of iterators of size N under the hood to produce a sliding window:
namespace details {
template<unsigned...>struct indexes { using type=indexes; };
template<unsigned max, unsigned... is>struct make_indexes:make_indexes<max-1, max-1, is...>{};
template<unsigned... is>struct make_indexes<0,is...>:indexes<is...>{};
template<unsigned max>using make_indexes_t=typename make_indexes<max>::type;
template<bool b, class T=void>
using enable_if_t=typename std::enable_if<b,T>::type;
struct list_tag {};
struct from_iterator_tag {};
template<unsigned N, class Iterator>
struct iterator_array {
private:
std::array<Iterator,N> raw;
size_t index = 0;
static Iterator to_elem(Iterator& it, Iterator end, bool advance=true) {
if (it == end) return end;
if (advance) return ++it;
return it;
}
template< unsigned...Is>
iterator_array( indexes<Is...>, from_iterator_tag, Iterator& it, Iterator end ):
raw( {to_elem(it, end, false), (void(Is), to_elem(it,end))...} )
{}
public:
Iterator begin() const { return raw[index]; }
Iterator end() const { return std::next(raw[(index+N-1)%N]); }
void push_back( Iterator it ) {
raw[index] = it;
index = (index+1)%N;
}
iterator_array( from_iterator_tag, Iterator& it, Iterator end ):iterator_array( make_indexes<N-1>{}, from_iterator_tag{}, it, end ) {}
iterator_array( iterator_array const& o )=default;
iterator_array() = default; // invalid!
iterator_array& operator=( iterator_array const& o )=delete;
typedef decltype(*std::declval<Iterator>()) reference_type;
reference_type operator[](std::size_t i)const{return *(raw[ (i+index)%N ]);}
};
struct sentinal_tag {};
template<class I>using value_type_t=typename std::iterator_traits<I>::value_type;
template<class I, unsigned N>
class slide_iterator:public std::iterator<
std::forward_iterator_tag,
iterator_array<N,I>,
iterator_array<N,I>*,
iterator_array<N,I> const&
> {
I current;
mutable bool bread = false;
typedef iterator_array<N,I> value_type;
mutable value_type data;
void ensure_read() const {
if (!bread) {
data.push_back(current);
}
bread = true;
}
public:
slide_iterator& operator++() { ensure_read(); ++current; bread=false; return *this; }
slide_iterator operator++(int) { slide_iterator retval=*this; ++*this; return retval; }
value_type const& operator*() const { ensure_read(); return data; }
bool operator==(slide_iterator const& o){return current==o.current;}
bool operator!=(slide_iterator const& o){return current!=o.current;}
bool operator<(slide_iterator const& o){return current<o.current;}
bool operator>(slide_iterator const& o){return current>o.current;}
bool operator<=(slide_iterator const& o){return current<=o.current;}
bool operator>=(slide_iterator const& o){return current>=o.current;}
explicit slide_iterator( I start, I end ):current(start), bread(true), data(from_iterator_tag{}, current, end) {}
explicit slide_iterator( sentinal_tag, I end ):current(end) {}
};
}
template<class Iterator, unsigned N>
struct slide_range_t {
using iterator=details::slide_iterator<Iterator, N>;
iterator b;
iterator e;
slide_range_t( Iterator start, Iterator end ):
b( start, end ),
e( details::sentinal_tag{}, end )
{}
slide_range_t( slide_range_t const& o )=default;
slide_range_t() = delete;
iterator begin() const { return b; }
iterator end() const { return e; }
};
template<unsigned N, class Iterator>
slide_range_t< Iterator, N > slide_range( Iterator b, Iterator e ) {
return {b,e};
}
live example
Note that the elements of your slide range are themselves iterable. A further improvement would be to specialize for random-access iterators and only store the begin/end pair in that case.
Sample use:
int main() {
std::vector<int> foo(33);
for (int i = 0; i < foo.size(); ++i)
foo[i]=i;
for( auto&& r:slide_range<3>(foo.begin(), foo.end()) ) {
for (int x : r) {
std::cout << x << ",";
}
std::cout << "\n";
}
// your code goes here
return 0;
}

Expression template with CRTP as lvalue

I'm writing a library that uses expression templates with CRTP. The source files can be found here: https://github.com/mspraggs/pyQCD/tree/master/lib/include/base
The expression templates are based on the example given in the Wikipedia article on the subject. I list the code here in case the Wiki article changes in future:
#include <vector>
#include <cassert>
template <typename E>
// A CRTP base class for Vecs with a size and indexing:
class VecExpression {
public:
typedef std::vector<double> container_type;
typedef container_type::size_type size_type;
typedef container_type::value_type value_type;
typedef container_type::reference reference;
size_type size() const { return static_cast<E const&>(*this).size(); }
value_type operator[](size_type i) const { return static_cast<E const&>(*this)[i]; }
operator E&() { return static_cast< E&>(*this); }
operator E const&() const { return static_cast<const E&>(*this); }
};
// The actual Vec class:
class Vec : public VecExpression<Vec> {
container_type _data;
public:
reference operator[](size_type i) { return _data[i]; }
value_type operator[](size_type i) const { return _data[i]; }
size_type size() const { return _data.size(); }
Vec(size_type n) : _data(n) {} // Construct a given size:
// Construct from any VecExpression:
template <typename E>
Vec(VecExpression<E> const& vec) {
E const& v = vec;
_data.resize(v.size());
for (size_type i = 0; i != v.size(); ++i) {
_data[i] = v[i];
}
}
};
template <typename E1, typename E2>
class VecDifference : public VecExpression<VecDifference<E1, E2> > {
E1 const& _u;
E2 const& _v;
public:
typedef Vec::size_type size_type;
typedef Vec::value_type value_type;
VecDifference(VecExpression<E1> const& u, VecExpression<E2> const& v) : _u(u), _v(v) {
assert(u.size() == v.size());
}
size_type size() const { return _v.size(); }
value_type operator[](Vec::size_type i) const { return _u[i] - _v[i]; }
};
template <typename E>
class VecScaled : public VecExpression<VecScaled<E> > {
double _alpha;
E const& _v;
public:
VecScaled(double alpha, VecExpression<E> const& v) : _alpha(alpha), _v(v) {}
Vec::size_type size() const { return _v.size(); }
Vec::value_type operator[](Vec::size_type i) const { return _alpha * _v[i]; }
};
// Now we can overload operators:
template <typename E1, typename E2>
VecDifference<E1,E2> const
operator-(VecExpression<E1> const& u, VecExpression<E2> const& v) {
return VecDifference<E1,E2>(u,v);
}
template <typename E>
VecScaled<E> const
operator*(double alpha, VecExpression<E> const& v) {
return VecScaled<E>(alpha,v);
}
What I want to do is add another expression template that allows assignment to part of the original template object (the Vec class in the code above, and the LatticeBase class in the code I've linked to). Possible usage:
Vec myvector(10);
Vec another_vector(5);
myvector.head(5) = another_vector; // Assign first 5 elements on myvector
myvector.head(2) = another_vector.head(2); // EDIT
So I'd create a new function Vec::head that would a return an expression template for a portion of the Vec object. I don't know how this would fit into the framework I currently have. In particular I have the following questions/comments:
I've seen examples of what I want to achieve in expression templates that don't use CRTP. What do I gain by using CRTP in this case? Is there any point? Should I ditch it and follow the other examples I've found?
In the current framework, the assignment to the _data member in the Vec class is handled by a copy constructor in the Vec class. This won't work if I want to use the expression template returned by Vec::head, since the assignment happens within the class that holds the data, not the expression template.
I've tried creating an assignment operator within the new expression template, but that won't work with the code above as all the expression template members are const references, and so the assignment operator is deleted at compile time. Can I just switch the members to being values instead of references? Will this impact on performance if additional storage is needed? Will this even work (if I change a stored copy of the expression rather than the expression itself)?
Overall I'm confused about how to go about adding an expression template that can be used as an lvalue in the code above. Any guidance on this would be greatly appreciated.
Try this:
#include <vector>
#include <cassert>
template <typename E>
// A CRTP base class for Vecs with a size and indexing:
class VecExpression {
public:
typedef std::vector<double> container_type;
typedef container_type::size_type size_type;
typedef container_type::value_type value_type;
typedef container_type::reference reference;
size_type size() const { return static_cast<E const&>(*this).size(); }
value_type operator[](size_type i) const { return static_cast<E const&>(*this)[i]; }
operator E&() { return static_cast<E&>(*this); }
operator E const&() const { return static_cast<const E&>(*this); }
};
class VecHead;
// The actual Vec class:
class Vec : public VecExpression<Vec> {
container_type _data;
public:
reference operator[](size_type i) { return _data[i]; }
value_type operator[](size_type i) const { return _data[i]; }
size_type size() const { return _data.size(); }
Vec(size_type n) : _data(n) {} // Construct a given size:
// Construct from any VecExpression:
template <typename E>
Vec(VecExpression<E> const& vec) {
E const& v = vec;
_data.resize(v.size());
for (size_type i = 0; i != v.size(); ++i) {
_data[i] = v[i];
}
}
VecHead head(size_type s);
};
class VecHead : public VecExpression< VecHead >
{
Vec::size_type _s;
Vec& _e;
public:
typedef Vec::size_type size_type;
typedef Vec::value_type value_type;
VecHead(std::size_t s, Vec& e)
: _s(s)
, _e(e)
{
assert(_e.size() >= _s);
}
size_type size() const { return _s; }
value_type operator[](Vec::size_type i) const { assert(i < _s); return _e[i]; }
VecHead& operator = (const VecHead& rhs)
{
return operator=(static_cast<const VecExpression<VecHead>&>(rhs));
}
template <typename E>
VecHead& operator = (const VecExpression<E>& rhs)
{
assert(rhs.size() >= _s);
for (size_type i = 0; i < _s && i < rhs.size(); ++i)
_e[i] = rhs[i];
return *this;
}
};
VecHead Vec::head(size_type s)
{
VecHead aHead(s, *this);
return aHead;
}
template <typename E1, typename E2>
class VecDifference : public VecExpression<VecDifference<E1, E2> > {
E1 const& _u;
E2 const& _v;
public:
typedef Vec::size_type size_type;
typedef Vec::value_type value_type;
VecDifference(VecExpression<E1> const& u, VecExpression<E2> const& v) : _u(u), _v(v) {
assert(u.size() == v.size());
}
size_type size() const { return _v.size(); }
value_type operator[](Vec::size_type i) const { return _u[i] - _v[i]; }
};
template <typename E>
class VecScaled : public VecExpression<VecScaled<E> > {
double _alpha;
E const& _v;
public:
VecScaled(double alpha, VecExpression<E> const& v) : _alpha(alpha), _v(v) {}
Vec::size_type size() const { return _v.size(); }
Vec::value_type operator[](Vec::size_type i) const { return _alpha * _v[i]; }
};
// Now we can overload operators:
template <typename E1, typename E2>
VecDifference<E1, E2> const
operator-(VecExpression<E1> const& u, VecExpression<E2> const& v) {
return VecDifference<E1, E2>(u, v);
}
template <typename E>
VecScaled<E> const
operator*(double alpha, VecExpression<E> const& v) {
return VecScaled<E>(alpha, v);
}
int main()
{
Vec myvector(10);
Vec another_vector(5);
for (int i = 0; i < 5; ++i)
another_vector[i] = i;
myvector.head(5) = another_vector; // Assign first 5 elements on myvector
assert(myvector.head(5).size() == 5);
for (int i = 0; i < 10; ++i)
{
assert(myvector[i] == (i < 5 ? static_cast<double>(i) : 0.));
}
//! Added test due to comment vec1.head(2) = vec2.head(2) doesn't work.
Vec vec1(10), vec2(10);
for (int i = 0; i < 10; ++i)
vec2[i] = 2 * (vec1[i] = i);
vec1.head(2) = vec2.head(2);
for (int i = 0; i < 10; ++i)
{
if (i < 2)
{
assert(vec1[i] == vec2[i]);
}
else
{
assert(vec1[i] != vec2[i]);
}
}
return 0;
}