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.
Related
Obviously we can concatenate two string literals in a constexpr function, but what about concatenation of a string literal with a string returned by another constexpr function as in the code below?
template <class T>
constexpr const char * get_arithmetic_size()
{
switch (sizeof(T))
{
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
default: static_assert(dependent_false_v<T>);
}
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic_v<T>, const char *> make_type_name()
{
const char * prefix = std::is_signed_v<T> ? "int" : "uint";
return prefix; // how to concatenate prefix with get_arithmetic_size<T>() ?
}
static_assert(strings_equal(make_type_name<int>, make_type_name<int32_t>);
The code makes compiler-independent string identifier of an arithmetic type.
EDIT1:
A little bit more complicated example is:
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
template <class T>
constexpr std::enable_if_t<is_specialization<T, std::vector>::value || is_specialization<T, std::list>::value, const char *> make_type_name()
{
return "sequence"; // + make_type_name<typename T::value_type>;
}
static_assert(strings_equal(make_type_name<std::vector<int>>(), make_type_name<std::list<int>>()));
Here is a quick compile time string class:
template<std::size_t N>
struct ct_str
{
char state[N+1] = {0};
constexpr ct_str( char const(&arr)[N+1] )
{
for (std::size_t i = 0; i < N; ++i)
state[i] = arr[i];
}
constexpr char operator[](std::size_t i) const { return state[i]; }
constexpr char& operator[](std::size_t i) { return state[i]; }
constexpr explicit operator char const*() const { return state; }
constexpr char const* data() const { return state; }
constexpr std::size_t size() const { return N; }
constexpr char const* begin() const { return state; }
constexpr char const* end() const { return begin()+size(); }
constexpr ct_str() = default;
constexpr ct_str( ct_str const& ) = default;
constexpr ct_str& operator=( ct_str const& ) = default;
template<std::size_t M>
friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
{
ct_str<N+M> retval;
for (std::size_t i = 0; i < N; ++i)
retval[i] = lhs[i];
for (std::size_t i = 0; i < M; ++i)
retval[N+i] = rhs[i];
return retval;
}
friend constexpr bool operator==( ct_str lhs, ct_str rhs )
{
for (std::size_t i = 0; i < N; ++i)
if (lhs[i] != rhs[i]) return false;
return true;
}
friend constexpr bool operator!=( ct_str lhs, ct_str rhs )
{
for (std::size_t i = 0; i < N; ++i)
if (lhs[i] != rhs[i]) return true;
return false;
}
template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs ) { return true; }
template<std::size_t M, std::enable_if_t< M!=N, bool > = true>
friend bool operator==( ct_str, ct_str<M> ) { return false; }
};
template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;
you can use it like this:
template <class T>
constexpr auto get_arithmetic_size()
{
if constexpr (sizeof(T)==1)
return ct_str{"1"};
if constexpr (sizeof(T)==2)
return ct_str{"2"};
if constexpr (sizeof(T)==4)
return ct_str{"4"};
if constexpr (sizeof(T)==8)
return ct_str{"8"};
if constexpr (sizeof(T)==16)
return ct_str{"16"};
}
template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
if constexpr (std::is_signed<T>{})
return ct_str{"int"} + get_arithmetic_size<T>();
else
return ct_str{"uint"} + get_arithmetic_size<T>();
}
which leads to statements like:
static_assert(make_type_name<int>() == make_type_name<int32_t>());
passing.
Live example.
Now one annoying thing is that the length of the buffer is in the type system. You could add a length field, and make N be "buffer size", and modify ct_str to only copy up to length and leave the trailing bytes as 0. Then override common_type to return the max N of both sides.
That would permit you do pass ct_str{"uint"} and ct_str{"int"} in the same type of value and make the implementation code a bit less annoying.
template<std::size_t N>
struct ct_str
{
char state[N+1] = {0};
template<std::size_t M, std::enable_if_t< (M<=N+1), bool > = true>
constexpr ct_str( char const(&arr)[M] ):
ct_str( arr, std::make_index_sequence<M>{} )
{}
template<std::size_t M, std::enable_if_t< (M<N), bool > = true >
constexpr ct_str( ct_str<M> const& o ):
ct_str( o, std::make_index_sequence<M>{} )
{}
private:
template<std::size_t M, std::size_t...Is>
constexpr ct_str( char const(&arr)[M], std::index_sequence<Is...> ):
state{ arr[Is]... }
{}
template<std::size_t M, std::size_t...Is>
constexpr ct_str( ct_str<M> const& o, std::index_sequence<Is...> ):
state{ o[Is]... }
{}
public:
constexpr char operator[](std::size_t i) const { return state[i]; }
constexpr char& operator[](std::size_t i) { return state[i]; }
constexpr explicit operator char const*() const { return state; }
constexpr char const* data() const { return state; }
constexpr std::size_t size() const {
std::size_t retval = 0;
while(state[retval]) {
++retval;
}
return retval;
}
constexpr char const* begin() const { return state; }
constexpr char const* end() const { return begin()+size(); }
constexpr ct_str() = default;
constexpr ct_str( ct_str const& ) = default;
constexpr ct_str& operator=( ct_str const& ) = default;
template<std::size_t M>
friend constexpr ct_str<N+M> operator+( ct_str lhs, ct_str<M> rhs )
{
ct_str<N+M> retval;
for (std::size_t i = 0; i < lhs.size(); ++i)
retval[i] = lhs[i];
for (std::size_t i = 0; i < rhs.size(); ++i)
retval[lhs.size()+i] = rhs[i];
return retval;
}
template<std::size_t M>
friend constexpr bool operator==( ct_str lhs, ct_str<M> rhs )
{
if (lhs.size() != rhs.size()) return false;
for (std::size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i]) return false;
return true;
}
template<std::size_t M>
friend constexpr bool operator!=( ct_str lhs, ct_str<M> rhs )
{
if (lhs.size() != rhs.size()) return true;
for (std::size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i]) return true;
return false;
}
};
template<std::size_t N>
ct_str( char const(&)[N] )->ct_str<N-1>;
The function implementations now become:
template <class T>
constexpr ct_str<2> get_arithmetic_size()
{
switch (sizeof(T)) {
case 1: return "1";
case 2: return "2";
case 4: return "4";
case 8: return "8";
case 16: return "16";
}
}
template <class T, std::enable_if_t<std::is_arithmetic<T>{}, bool> = true>
constexpr auto make_type_name()
{
constexpr auto base = std::is_signed<T>{}?ct_str{"int"}:ct_str{"uint"};
return base + get_arithmetic_size<T>();
}
which is a lot more natural to write.
Live example.
No, it's impossible. You can implement something like below (it's C++14).
#include <cmath>
#include <cstring>
#include <iostream>
#include <type_traits>
constexpr const char* name[] = {
"uint1", "uint2", "uint4", "uint8", "uint16",
"int1", "int2", "int4", "int8", "int16"
};
template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
return name[std::is_signed<T>::value * 5 +
static_cast<int>(std::log(sizeof(T)) / std::log(2) + 0.5)];
}
static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);
int main() {
std::cout << make_type_name<int>();
return 0;
}
https://ideone.com/BaADaM
If you don't like using <cmath>, you may replace std::log:
#include <cstring>
#include <iostream>
#include <type_traits>
constexpr const char* name[] = {
"uint1", "uint2", "uint4", "uint8", "uint16",
"int1", "int2", "int4", "int8", "int16"
};
constexpr size_t log2(size_t n) {
return (n<2) ? 0 : 1 + log2(n/2);
}
template <class T>
constexpr std::enable_if_t<std::is_arithmetic<T>::value, const char *> make_type_name() {
return name[std::is_signed<T>::value * 5 + log2(sizeof(T))];
}
static_assert(std::strcmp(make_type_name<int>(), make_type_name<int32_t>()) == 0);
int main() {
std::cout << make_type_name<int>();
return 0;
}
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;
}
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)
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.
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.