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
Related
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.
I have the following class:
class Data;
class A
{
public:
A(Data& _data) : data(_data) {}
Data& getData() {return data;}
const Data& getData() const {return data;}
private:
Data& data;
};
Now imagine I need to keep not one, but multiple instances of Data. I keep them in a vector of reference wrappers, but I would also like to keep the const correctness: pass the data as unmodifiable in const context.
class A
{
public:
void addData(Data& _data) {data.push_back(std::ref(_data));}
const std::vector<std::reference_wrapper<Data>>& getData() {return data;}
//doesn't compile
//const std::vector<std::reference_wrapper<const Data>>& getData() const {return data;}
private:
std::vector<std::reference_wrapper<Data>> data;
}
How to implement this without having physical copying of the data? I.e. I don't want to return a copy of the vector by value and I don't want to keep two separate vectors in class A. Both are performance-impacting solutions for what is basically just a semantic problem.
Here's a const propagating reference_wrapper, based on cppreference's possible implementation
#include <utility>
#include <functional>
#include <type_traits>
namespace detail {
template <class T> T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>> && !std::is_same_v<reference_wrapper<const T>, std::remove_cvref_t<U>>>()
)>
reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
reference_wrapper(reference_wrapper&) noexcept = default;
reference_wrapper(reference_wrapper&&) noexcept = default;
// assignment
reference_wrapper& operator=(reference_wrapper& x) noexcept = default;
reference_wrapper& operator=(reference_wrapper&& x) noexcept = default;
// access
operator T& () noexcept { return *_ptr; }
T& get() noexcept { return *_ptr; }
operator const T& () const noexcept { return *_ptr; }
const T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
std::invoke_result_t<T&, ArgTypes...>
operator() ( ArgTypes&&... args ) {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
template< class... ArgTypes >
std::invoke_result_t<const T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
T* _ptr;
};
template <class T>
class reference_wrapper<const T> {
public:
// types
typedef const T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<const T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>> && !std::is_same_v<reference_wrapper<T>, std::remove_cvref_t<U>>>()
)>
reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<const T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<const T>(std::forward<U>(u)))) {}
reference_wrapper(const reference_wrapper<T>& o) noexcept
: _ptr(std::addressof(o.get())) {}
reference_wrapper(const reference_wrapper&) noexcept = default;
reference_wrapper(reference_wrapper&&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
reference_wrapper& operator=(reference_wrapper&& x) noexcept = default;
// access
operator const T& () const noexcept { return *_ptr; }
const T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
std::invoke_result_t<const T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
const T* _ptr;
};
// deduction guides
template<class T>
reference_wrapper(T&) -> reference_wrapper<T>;
You can then add const qualified access via span.
class A
{
public:
void addData(Data& _data) {data.emplace_back(_data);}
std::span<reference_wrapper<Data>> getData() { return { data.data(), data.size() }; }
std::span<const reference_wrapper<Data>> getData() const { return { data.data(), data.size() }; }
private:
std::vector<reference_wrapper<Data>> data;
}
Note that you can't copy or move the const reference_wrapper<Data>s from the second getData, and there is only access to const Data &.
Consider the visitor pattern :
struct ConstVisitor {
virtual ~ConstVisitor() = default;
virtual bool visit(const Data & data) = 0;//returns true if search should keep going on
};
void A::accept(ConstVisitor & visitor) const;
This way it does not matter to the outside world what kind of container Data is stored in (here a std::vector). The visitor pattern is very similar to an Enumerator in 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;
}
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.
I'm working on a segment-based memory allocator for C++. In this allocator, when you deallocate a chunk of memory, you have to know which segment it came from. Therefore, I'm storing a pointer to the segment as a member of the fancy pointer returned from the allocator's allocate function.
Just to show the interface I'm talking about: here's the fancy_memory_resource that backs my allocator...
template<class Ptr>
class fancy_memory_resource {
public:
Ptr allocate(size_t bytes, size_t align = alignof(max_align_t)) {
return do_allocate(bytes, align);
}
void deallocate(Ptr p, size_t bytes, size_t align = alignof(max_align_t)) {
return do_deallocate(p, bytes, align);
}
bool is_equal(const fancy_memory_resource& rhs) const noexcept {
return do_is_equal(rhs);
}
virtual ~fancy_memory_resource() = default;
private:
virtual Ptr do_allocate(size_t bytes, size_t align) = 0;
virtual void do_deallocate(Ptr p, size_t bytes, size_t align) = 0;
virtual bool do_is_equal(const fancy_memory_resource& rhs) const noexcept = 0;
};
(Notice that std::pmr::memory_resource can be implemented as a typedef for fancy_memory_resource<void*>. This is intentional on my part.)
Meanwhile, the Ptr in question is a fancy pointer type named segmented_fancy_pointer<T> (not pictured) that inherits from the CRTP type fancy_ptr_base<T, segmented_fancy_pointer<T>>...
template<class T, class CRTP>
struct fancy_ptr_base {
constexpr T *ptr() const noexcept { return m_ptr; }
constexpr explicit operator T*() const noexcept { return ptr(); }
constexpr explicit operator bool() const noexcept { return ptr() != nullptr; }
constexpr bool operator==(CRTP b) const { return ptr() == b.ptr(); }
constexpr bool operator!=(CRTP b) const { return ptr() != b.ptr(); }
constexpr bool operator==(decltype(nullptr)) const { return ptr() == nullptr; }
constexpr bool operator!=(decltype(nullptr)) const { return ptr() != nullptr; }
constexpr bool operator<(CRTP b) const { return ptr() < b.ptr(); }
constexpr bool operator<=(CRTP b) const { return ptr() <= b.ptr(); }
constexpr bool operator>(CRTP b) const { return ptr() > b.ptr(); }
constexpr bool operator>=(CRTP b) const { return ptr() >= b.ptr(); }
constexpr T& operator*() const noexcept { return *ptr(); }
constexpr T* operator->() const noexcept { return ptr(); }
constexpr CRTP& operator+=(ptrdiff_t i) { m_ptr += i; return as_crtp(); }
constexpr CRTP& operator-=(ptrdiff_t i) { m_ptr -= i; return as_crtp(); }
constexpr CRTP& operator++() { ++m_ptr; return as_crtp(); }
constexpr CRTP& operator--() { --m_ptr; return as_crtp(); }
constexpr CRTP operator++(int) { auto r(as_crtp()); ++*this; return r; }
constexpr CRTP operator--(int) { auto r(as_crtp()); --*this; return r; }
constexpr CRTP operator+(ptrdiff_t i) const { auto r(as_crtp()); r += i; return r; }
constexpr CRTP operator-(ptrdiff_t i) const { auto r(as_crtp()); r -= i; return r; }
constexpr ptrdiff_t operator-(CRTP b) const { return ptr() - b.ptr(); }
protected:
T *m_ptr = nullptr;
private:
constexpr CRTP& as_crtp() { return *static_cast<CRTP*>(this); }
constexpr const CRTP& as_crtp() const { return *static_cast<const CRTP*>(this); }
};
template<class CRTP>
struct fancy_ptr_base<void, CRTP> {
constexpr void *ptr() const noexcept { return m_ptr; }
constexpr explicit operator void*() const noexcept { return ptr(); }
constexpr explicit operator bool() const noexcept { return ptr() != nullptr; }
constexpr bool operator==(CRTP b) const { return ptr() == b.ptr(); }
constexpr bool operator!=(CRTP b) const { return ptr() != b.ptr(); }
constexpr bool operator==(decltype(nullptr)) const { return ptr() == nullptr; }
constexpr bool operator!=(decltype(nullptr)) const { return ptr() != nullptr; }
constexpr bool operator<(CRTP b) const { return ptr() < b.ptr(); }
constexpr bool operator<=(CRTP b) const { return ptr() <= b.ptr(); }
constexpr bool operator>(CRTP b) const { return ptr() > b.ptr(); }
constexpr bool operator>=(CRTP b) const { return ptr() >= b.ptr(); }
protected:
void *m_ptr = nullptr;
};
Now for the real question. When I go to use my segmented_allocator<T> (not pictured) with libc++'s std::vector, it all works fine. When I try to use it with libstdc++'s std::vector, it fails:
In file included from /opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_algobase.h:67:0,
from /opt/wandbox/gcc-head/include/c++/8.0.0/vector:60,
from prog.cc:1984:
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_iterator.h: In instantiation of 'class __gnu_cxx::__normal_iterator<scratch::segmented_fancy_pointer<int>, std::vector<int, scratch::pmr::propagating_polymorphic_allocator<int, scratch::segmented_fancy_pointer<int> > > >':
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/vector.tcc:105:25: required from 'std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int}; _Tp = int; _Alloc = scratch::pmr::propagating_polymorphic_allocator<int, scratch::segmented_fancy_pointer<int> >; std::vector<_Tp, _Alloc>::reference = int&]'
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_vector.h:954:21: required from 'void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = int; _Alloc = scratch::pmr::propagating_polymorphic_allocator<int, scratch::segmented_fancy_pointer<int> >; std::vector<_Tp, _Alloc>::value_type = int]'
prog.cc:1990:18: required from here
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/stl_iterator.h:770:57: error: no type named 'iterator_category' in 'struct std::iterator_traits<scratch::segmented_fancy_pointer<int> >'
typedef typename __traits_type::iterator_category iterator_category;
^~~~~~~~~~~~~~~~~
Now, I can fix this by adding the "iterator traits" typedefs into fancy_ptr_base<T, CRTP>, like this:
using pointer = CRTP;
using reference = T&;
using value_type = std::remove_cv_t<T>;
using iterator_category = std::random_access_iterator_tag;
using difference_type = ptrdiff_t;
But should I have to? Is it required that every fancy pointer type be an iterator type as well? Or is libc++ doing the right thing and libstdc++'s vector just has a bug?
(I have already convinced myself that most iterators are not fancy pointers. This question is motivated by my sudden doubt that perhaps all fancy pointers are indeed iterators.)
Yes, you are required to implement all requirements of a Random Access Iterator. C++ Standard [allocator.requirements]/5:
An allocator type X shall.... X::pointer and X::const_pointer shall also satisfy the requirements for a random access iterator.
So in particular, your fancy pointer type needs the five member types required of every iterator.
You also seem to be missing fancy_memory_resource<Ptr>::value_type, several needed non-member functions, and a number of noexcept keywords. Please review the requirements for allocator types and their pointer types carefully.