using std::array or similar in constexpr functions

666 views
Skip to first unread message

Peter Sommerlad

unread,
Oct 6, 2014, 12:05:39 PM10/6/14
to <std-discussion@isocpp.org>
Hi,

I am playing around with C++14 relaxed constexpr functions and I wonder, how one need to specify std::array-like structure to create a table in a constexpr function at compile time. While I am able to do that using recursion and variadic templates, I tried the straight forward way using an array and failed with std::array, because I can not call the non-const operator[] in a constexpr function. With a simple array wrapping struct I can write such code, but I must directly access the array member.

Is this a limitation of my version of clang or a limitation in the c++14 standard?

Any ideas?

Here is a simple example code:

#include <array>

template <typename T, size_t n>
struct ar{
T a[n];
constexpr ar():a{{}}{}
constexpr auto data() const { return &a[0];}
constexpr T const & operator[](size_t i) const { return a[i];}
};

template <size_t n>
using arr=ar<size_t,n>;//std::array<size_t,n>; // doesn't wirk with std::array :-(


template <size_t n>
constexpr auto make_tab(){
arr<n> result;
for(size_t i=0; i < n; ++i)
result.a[i] = (i+1)*(i+1); // can not define operator[] for mutable array type ar must use .a
return result;
}

template <size_t n>
constexpr auto squares=make_tab<n>();


int main(){
int dummy[squares<5>[3]];
}
=============

Thanks
Peter.


--
Prof. Peter Sommerlad

Institut für Software: Bessere Software - Einfach, Schneller!
HSR Hochschule für Technik Rapperswil
Oberseestr 10, Postfach 1475, CH-8640 Rapperswil

http://ifs.hsr.ch http://cevelop.com http://linticator.com
tel:+41 55 222 49 84 == mobile:+41 79 432 23 32
fax:+41 55 222 46 29 == mailto:peter.s...@hsr.ch






Richard Smith

unread,
Oct 6, 2014, 12:26:56 PM10/6/14
to std-dis...@isocpp.org
On Mon, Oct 6, 2014 at 9:05 AM, Peter Sommerlad <peter.s...@hsr.ch> wrote:
Hi,

I am playing around with C++14 relaxed constexpr functions and I wonder, how one need to specify std::array-like structure to create a table in a constexpr function at compile time. While I am able to do that using recursion and variadic templates, I tried the straight forward way using an array and failed with std::array, because I can not call the non-const operator[] in a constexpr function. With a simple array wrapping struct I can write such code, but I must directly access the array member.

Is this a limitation of my version of clang or a limitation in the c++14 standard?

You don't need to access the member directly; you can add a non-const operator[] to your struct. However, the fact that std::array's non-const operator[] is not constexpr is a limitation of C++14; it wasn't possible to make the non-const operator[] constexpr at the time when constexpr was added to the const overload.
 
Any ideas?

Here is a simple example code:

#include <array>

template <typename T, size_t n>
struct ar{
        T a[n];
        constexpr ar():a{{}}{}
        constexpr auto data() const { return &a[0];}
        constexpr T const & operator[](size_t i) const { return a[i];}
        constexpr T &operator[](size_t i) { return a[i]; }
};

template <size_t n>
using arr=ar<size_t,n>;//std::array<size_t,n>; // doesn't wirk with std::array :-(


template <size_t n>
constexpr auto make_tab(){
        arr<n> result;
        for(size_t i=0; i < n; ++i)
                result.a[i] = (i+1)*(i+1); // can not define operator[] for mutable array type ar must use .a
                  result[i] = (i+1)*(i+1); 
        return result;
}

template <size_t n>
constexpr auto squares=make_tab<n>();


int main(){
        int dummy[squares<5>[3]];
}
=============

Thanks
Peter.


--
Prof. Peter Sommerlad

Institut für Software: Bessere Software - Einfach, Schneller!
HSR Hochschule für Technik Rapperswil
Oberseestr 10, Postfach 1475, CH-8640 Rapperswil

http://ifs.hsr.ch http://cevelop.com http://linticator.com
tel:+41 55 222 49 84 == mobile:+41 79 432 23 32
fax:+41 55 222 46 29 == mailto:peter.s...@hsr.ch






--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

Peter Sommerlad

unread,
Oct 6, 2014, 2:20:59 PM10/6/14
to std-dis...@isocpp.org
Hi,

thanks to Richard. With his suggestions my code now compiles.

On 06.10.2014, at 18:26, Richard Smith <ric...@metafoo.co.uk> wrote:

> However, the fact that std::array's non-const operator[] is not constexpr is a limitation of C++14; it wasn't possible to make the non-const operator[] constexpr at the time when constexpr was added to the const overload.
>

Does that mean, I should file a DR against the standard?

// element access:

reference operator[](size_type n);

should read

// element access:

constexpr reference operator[](size_type n);


I wonder, why at() is constexpr, since it defines to throw an exception which can not occur in a constexpr function. (I assume constexpr and noexcept(false) be mutually exclusive).

I think we need much better guidelines on adding constexpr to the standard library functions to allow more useful constexpr computation. It is annoying when you can not use something from the standard library where a simple substitution would work.

Also it is unclear which of the many members of std::array should be marked constexpr?

A first guess for me is that all member functions of std::array should be constexpr (with the exception of both overloads of at() ?)

Regards

Daniel Krügler

unread,
Oct 6, 2014, 2:24:59 PM10/6/14
to std-dis...@isocpp.org
2014-10-06 20:20 GMT+02:00 Peter Sommerlad <peter.s...@hsr.ch>:
> Hi,
>
> thanks to Richard. With his suggestions my code now compiles.
>
> On 06.10.2014, at 18:26, Richard Smith <ric...@metafoo.co.uk> wrote:
>
>> However, the fact that std::array's non-const operator[] is not constexpr is a limitation of C++14; it wasn't possible to make the non-const operator[] constexpr at the time when constexpr was added to the const overload.
>>
>
> Does that mean, I should file a DR against the standard?

You should do that. We need issues for all constexpr stuff given that
we do not allow any more that an implementation adds constexpr.

> // element access:
>
> reference operator[](size_type n);
>
> should read
>
> // element access:
>
> constexpr reference operator[](size_type n);
>
> I wonder, why at() is constexpr, since it defines to throw an exception which can not occur in a constexpr function. (I assume constexpr and noexcept(false) be mutually exclusive).
>

You are assuming the wrong thing. The combinations makes very well
sense and has just the effect that for a constant expression the
throw-path (if evaluated to that) will make the code ill-formed.

- Daniel

Nevin Liber

unread,
Oct 6, 2014, 3:10:14 PM10/6/14
to std-dis...@isocpp.org
On 6 October 2014 13:24, Daniel Krügler <daniel....@gmail.com> wrote:
> Does that mean, I should file a DR against the standard?

You should do that. We need issues for all constexpr stuff given that
we do not allow any more that an implementation adds constexpr.

What is the defect?

While I'd really like constexpr on a bunch more things, this seems like we are using DRs as an end-run around the Committee decision (which I voted against) not to add constexpr to more functions.

I'd be more comfortable if the Committee itself agreed that this is the process for adding constexpr to functions between revisions of the standard.  Maybe we should bring this up in plenary in Urbana?
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Daniel Krügler

unread,
Oct 6, 2014, 3:25:58 PM10/6/14
to std-dis...@isocpp.org
2014-10-06 21:09 GMT+02:00 Nevin Liber <ne...@eviloverlord.com>:
> On 6 October 2014 13:24, Daniel Krügler <daniel....@gmail.com> wrote:
>>
>> > Does that mean, I should file a DR against the standard?
>>
>> You should do that. We need issues for all constexpr stuff given that
>> we do not allow any more that an implementation adds constexpr.
>
>
> What is the defect?

Let me respond with another question: What was the defect of

http://cplusplus.github.io/LWG/lwg-defects.html#2187

;-)

This is just an example and I'm not intending to pick on you, Nevin
(we have numerous such examples, including several from myself, I
guess).

Fact is, that we have sometimes open (LWG) issues for minor feature
requests. I'm not extremely happy with that, but that is current
reality.

I had foreseen that disallowing adding constexpr by implementations
will presumably cause a bunch of such kind of issues and I think that
is just the natural consequence of it.

Given that we have now a Bugzilla tracking for Library evolution-like stuff(?)

https://issues.isocpp.org/buglist.cgi?component=Library&list_id=278&product=C%2B%2B&resolution=---

this might even be a better choice for the future.

> While I'd really like constexpr on a bunch more things, this seems like we
> are using DRs as an end-run around the Committee decision (which I voted
> against) not to add constexpr to more functions.

This is presumably hard to measure exactly, but I guess there is some
truth in it.

> I'd be more comfortable if the Committee itself agreed that this is the
> process for adding constexpr to functions between revisions of the standard.
> Maybe we should bring this up in plenary in Urbana?

Feel free to do so. My gut feeling is that it might be better to write
a paper for better documentation. Whether I like it or not: It was a
decision at that time by the majority of committee members, so there
were (at least at that time) some reasons for this choice. And such a
direction has some weight that cannot be ignored (but might well be
analyzed).

- Daniel

Myriachan

unread,
Oct 6, 2014, 3:48:51 PM10/6/14
to std-dis...@isocpp.org

Why does the Standard have this restriction?  It seems to me that throw ought to be legal in constexpr functions.  If a throw gets evaluated as a constant-expression, that's ill-formed.  It seems silly that we allow constexpr functions to use throw only if there is some conditional expression involved.  Heck, throw would make a really nice static_assert-like mechanism for constexpr functions.

Melissa

Daniel Krügler

unread,
Oct 6, 2014, 3:53:22 PM10/6/14
to std-dis...@isocpp.org
Could you please provide a more concrete example that you would like
to make well-formed? I'm not sure whether I understand your proposal.

Thanks,

- Daniel

rhalb...@gmail.com

unread,
Oct 7, 2014, 3:50:21 AM10/7/14
to std-dis...@isocpp.org
On Monday, October 6, 2014 6:05:39 PM UTC+2, PeterSommerlad wrote:
Hi,

I am playing around with C++14 relaxed constexpr functions and I wonder, how one need to specify std::array-like structure to create a table in a constexpr function at compile time. While I am able to do that using recursion and variadic templates, I tried the straight forward way using an array and failed with std::array, because I can not call the non-const operator[] in a constexpr function. With a simple array wrapping struct I can write such code, but I must directly access the array member.

It is true that std::array would benefit from being made fully constexpr. However, in C++14 you do not have to do manual recursion on variadic templates anymore. Instead, you can use std::index_sequence and std::make_index_sequence, e.g. like this:

namespace detail {

template<class Function, std::size_t... Indices>
constexpr auto make_array_helper(Function f, std::index_sequence<Indices...>)
-> std::array<decltype(f(std::size_t{})), sizeof...(Indices)>
{
        return {{ f(Indices)... }};
}

}       // namespace detail

template<std::size_t N, class Function>
constexpr auto make_array(Function f)
{
        return detail::make_array_helper(f, std::make_index_sequence<N>{});
}

constexpr auto square_plus_one(std::size_t x)
{
    return (x + 1) * (x + 1);    
}

int main()
{
    auto constexpr squares = make_array<5>(square_plus_one);
    
    for (x : squares)
        std::cout << x << ",";
}


Just be careful though: make_array doesn't accept a lambda because C++14 does not accept constexpr lambdas. But also that can be worked around with by defining a class with a constexpr function operator (much like the pre-C++14 work arounds for polymorphic lambdas).

Peter Sommerlad

unread,
Oct 7, 2014, 6:24:33 AM10/7/14
to std-dis...@isocpp.org

On 07.10.2014, at 09:50, rhalb...@gmail.com wrote:

It is true that std::array would benefit from being made fully constexpr. However, in C++14 you do not have to do manual recursion on variadic templates anymore. Instead, you can use std::index_sequence and std::make_index_sequence, e.g. like this:
That is exactly what I did. Nevertheless, you need several steps to create a two-dimensional array and convert that into a string as a table (which was my original example). Using my own version of array allows to solve that is much more straight forward.

Regards

Edward Catmur

unread,
Oct 7, 2014, 8:31:48 AM10/7/14
to std-dis...@isocpp.org
There may be some confusion here. This is a valid minimal implementation of the constexpr overload of array::at():

    constexpr T const& at(std::size_t n) const {
       
if (n >= size()) throw std::out_of_range("");
       
return data[n]; }

The call is well-formed within a constant expression as long as n is less than size() i.e. less than N. The use of a conditional (ternary ?:) expression was necessary under C++11 as constexpr functions could back then contain only a single return statement.

Louis Dionne

unread,
Oct 7, 2014, 9:21:29 AM10/7/14
to std-dis...@isocpp.org
If you want to play around, here's a fully-constexpr array:


Louis

Peter Sommerlad

unread,
Oct 7, 2014, 10:24:35 AM10/7/14
to std-dis...@isocpp.org
thanks, already had my own myarray minimal version....
--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-discussion/.

Nevin Liber

unread,
Oct 7, 2014, 12:46:03 PM10/7/14
to std-dis...@isocpp.org
On 6 October 2014 14:25, Daniel Krügler <daniel....@gmail.com> wrote:
Let me respond with another question: What was the defect of

http://cplusplus.github.io/LWG/lwg-defects.html#2187

;-)

This is just an example and I'm not intending to pick on you, Nevin
(we have numerous such examples, including several from myself, I
guess).

Don't worry; I don't mind. :-)

The defect in that case is that, flip() and the iterator category notwithstanding, the intended interface for vector<bool> should not be diverging from the interface for vector<EverythingElse>.

Compare that with <https://issues.isocpp.org/show_bug.cgi?id=39>.  Allowing std::string(nullptr, 0) is an enhancement, not a defect, because the original design deliberately forbid passing in a nullptr.
 
Feel free to do so. My gut feeling is that it might be better to write
a paper for better documentation. Whether I like it or not: It was a
decision at that time by the majority of committee members, so there
were (at least at that time) some reasons for this choice. And such a
direction has some weight that cannot be ignored (but might well be
analyzed).

The reason discussed during that plenary session is the desire for portability of the standard library with respect to constexpr.  As long as all the changes are going into the standard, that portability is preserved.

But, as usual, I only speak for myself, and not the committee.
Reply all
Reply to author
Forward
0 new messages