Cannot access member functions of constexpr CRTP Class after casting - c++

Building on the answer I got to my question Constexpr Class taking const references not compiling, I was trying go further (playing around with expression templates).
So as the answer suggested, I now use static variables as inputs, and everything compiles correctly (on MSVC 2019).
However, upon adding constexpr static auto val = expr[0] I get error C2131: expression did not evaluate to a constant. The issue seems to be with m_lhs[idx] and m_rhs[idx] as those do not seem to evaluate to a constant, despite the fact that i've marked all operator[] as constexpr.
Is it possible to fix this?
#include <cstddef>
template<class VecType>
struct Vector
{
constexpr const VecType& get() const noexcept
{
return static_cast<const VecType&>(*this);
}
};
template<size_t Size>
class FixedSizeVector : public Vector<FixedSizeVector<Size>>
{
public:
constexpr explicit FixedSizeVector(const int(&array)[Size]) noexcept
: m_vector()
{
for (size_t i = 0; i < Size; ++i)
m_vector[i] = array[i];
}
constexpr const int& operator[](size_t idx) const noexcept {
return m_vector[idx];
}
private:
int m_vector[Size];
};
template<class VecType1, class VecType2>
class ExpressionAdd : public Vector<ExpressionAdd<VecType1, VecType2>>
{
public:
constexpr ExpressionAdd(const VecType1& lhs, const VecType2& rhs) noexcept
: m_lhs(lhs), m_rhs(rhs) { }
constexpr int operator[](size_t idx) const noexcept {
return m_lhs[idx] + m_rhs[idx];
}
private:
const VecType1& m_lhs;
const VecType2& m_rhs;
};
template<class VecType1, class VecType2>
constexpr auto operator+(const Vector<VecType1>& lhs, const Vector<VecType2>& rhs)
{
return ExpressionAdd(lhs.get(), rhs.get());
}
int main()
{
constexpr static int arr[]{ 4,2,9 };
constexpr static FixedSizeVector a(arr);
constexpr static auto expr = a + a;
constexpr static auto val = expr[0]; /* <--- this line causes an error */
return 0;
}

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 array of constexpr function pointers

I'm trying to get my head around how constexpr work. And I need to convert a lot of code from const to constexpr. But I've hit a problem where I cant see the solution.
I have the following class:
class Control_base
{
public:
static constexpr Control_base high_clock();
static constexpr uint8_t size();
static const Control_base from_index(uint8_t index_);
Control_base& operator=(const Control_base& rhs) = default;
constexpr Device_address value() const;
private:
constexpr explicit Control_base(Device_address value_);
Device_address value;
};
constexpr Control_base::Control_base(Device_address value_) :
value(value_) {}
constexpr Device_address Control_base::value() const { return value; }
inline const Control_base Control_base::high_clock() { return Control_base(reinterpret_cast<Device_address>(0x10009000)); }
typedef const Control_base (*Control_base_func)(void);
const Control_base_func Control_base_arr[] = {&Control_base::high_clock};
inline const Control_base Control_base::from_index(uint8_t index_)
{
return Control_base_arr[index_]();
}
constexpr uint8_t Control_base::size() { return 1; }
};
I wish to make the following changes:
From
inline const Control_base Control_base::high_clock() { return Control_base(reinterpret_cast<Device_address>(0x10009000)); }
typedef const Control_base (*Control_base_func)(void);
const Control_base_func Control_base_arr[] = {&Control_base::high_clock};
To
constexpr Control_base Control_base::high_clock() { return Control_base(reinterpret_cast<Device_address>(0x10009000)); }
typedef const Control_base (*Control_base_func)(void);
constexpr Control_base_func Control_base_arr[] = {&Control_base::high_clock};
However, I get the following error in
constexpr Control_base_func Control_base_arr[] = {&Control_base::high_clock};
^
**value of type "ns::Control_base (*)()" cannot be used to initialize an entity of type "const ns::Control_base_func"**
I can't figure out what the best solution is here. And why it works with const but not constexpr
Regards
Solved it, I had overlooked that I've changed the return type from const to non-const
It should look like this
constexpr const Control_base Control_base::high_clock() { return Control_base(reinterpret_cast<Device_address>(0x10009000)); }
typedef const Control_base (*Control_base_func)(void);
constexpr Control_base_func Control_base_arr[] = {&Control_base::high_clock};
Regards

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.

Gcc 7.2 c++17 constexpr

I try to implement a constexpr stack only for understand constexpr.
I get a compile error from the following code that i don't understand:
If I correctly understand constexpr does not imply const
It compiles the init-list constexpr constructor that contains calls to push
It compiles the lambda spop that perform a pop
What am I missing?
live example
g++ prog.cc -Wall -Wextra -I/opt/wandbox/boost-1.65.0/gcc-7.2.0/include -std=gnu++1z
#include <array>
#include <stdexcept>
#include <type_traits>
namespace ds {
template <typename T, std::size_t N>
class array_stack final {
public:
using value_type = T;
using reference = value_type&;
using const_reference = value_type const&;
using size_type = std::size_t;
constexpr bool empty () const
{
return items_ == size_type{0};
}
constexpr bool full () const
{
return top_item_ == N;
}
constexpr size_type size () const
{
return items_;
}
constexpr reference top () &
{
if (empty())
throw std::logic_error{"Attempting top() on empty stack"};
return array_[top_item_ - 1];
}
constexpr const_reference top () const&
{
if (empty())
throw std::logic_error{"Attempting top() on empty stack"};
return array_[top_item_ - 1];
}
constexpr void push (value_type const& value)
{
if (full())
throw std::logic_error{"Attempting push() on full stack"};
array_[top_item_] = value;
top_item_++;
items_++;
}
constexpr void push (value_type&& value)
{
if (full())
throw std::logic_error{"Attempting push() on full stack"};
array_[top_item_] = std::move(value);
top_item_++;
items_++;
}
constexpr void pop ()
{
if (empty())
throw std::logic_error{"Attempting pop() on empty stack"};
top_item_--;
items_--;
}
constexpr void clear ()
{
items_ = size_type{0};
top_item_ = size_type{0};
}
constexpr array_stack ()
: items_{size_type{0}}, top_item_{size_type{0}}, array_{}
{}
constexpr array_stack (std::initializer_list<value_type> values) : array_stack ()
{
for (auto const& v : values)
push(v);
}
constexpr array_stack (array_stack const& rhs) : array_stack ()
{
array_ = rhs.array_;
items_ = rhs.items_;
top_item_ = rhs.top_item_;
}
constexpr array_stack (array_stack&& rhs)
: items_ {rhs.items_}, top_item_ {rhs.top_item_}, array_ {std::move(rhs.array_)}
{
rhs.items_ = size_type{0};
rhs.top_item_ = size_type{0};
}
constexpr array_stack& operator= (array_stack rhs)
{
array_ = std::move(rhs.array_);
items_ = std::move(rhs.items_);
top_item_ = std::move(rhs.top_item_);
return *this;
}
~array_stack () = default;
void swap (array_stack& rhs) noexcept(std::is_nothrow_swappable_v<value_type>)
{
using std::swap;
swap(items_, rhs.items_);
swap(top_item_, rhs.top_item_);
swap(array_, rhs.array_);
}
private:
size_type items_;
size_type top_item_;
std::array<value_type, N> array_;
};
template <typename T, std::size_t N>
void swap (array_stack<T, N>& lhs, array_stack<T, N>& rhs) noexcept(noexcept(lhs.swap(rhs)))
{
lhs.swap(rhs);
}
}
constexpr bool f()
{
constexpr ds::array_stack <int, 10> dstack{0,1,2,3,4,5,6,7,8,9};
constexpr ds::array_stack <int, 10> dstack2{dstack};
constexpr auto spop =[](auto s){ s.pop(); return s.size(); };
static_assert(dstack.size() == 10);
static_assert(!dstack.empty());
static_assert(dstack.full());
static_assert(dstack.top() == 9);
static_assert(dstack2.size() == 10);
static_assert(spop(dstack) == 9);
dstack2.pop();
return true;
}
int main()
{
constexpr ds::array_stack <int, 10> cstack;
static_assert(cstack.size() == 0);
static_assert(cstack.empty());
static_assert(!cstack.full());
static_assert(f());
return 0;
}
I get this error (i understand what it means but why?)
prog.cc: In function 'constexpr bool f()':
prog.cc:147:15: error: passing 'const ds::array_stack<int, 10>' as 'this' argument discards qualifiers [-fpermissive]
dstack2.pop();
^
prog.cc:66:24: note: in call to 'constexpr void ds::array_stack<T, N>::pop() [with T = int; long unsigned int N = 10]'
constexpr void pop ()
^~~
If I correctly understand constexpr does not imply const
No. Objects declared constexpr are indeed const. That's why dstack2.pop() is ill-formed - the very boring and C++03 reason that you're calling a non-const member function on a const object.
Once you remove the dstack2.pop() line, everything compiles.
It compiles the init-list constexpr constructor that contains calls to push
It compiles the lambda spop that perform a pop
In both of these cases, you're allowed to modify the object. In the first case, you're still in the constructor - so modifications are fine, the object is never const during construction (otherwise you couldn't construct it). In the lambda case, the argument isn't const - it's just auto.
[expr.const]/2:
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine, would evaluate one of the following expressions:
[...]
modification of an object unless it is applied to a non-volatile lvalue of literal type that refers to a non-volatile object whose lifetime began within the evaluation of e;

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.