Constexpr Library Additions: utilities, v3

ISO/IEC JTC1 SC22 WG21 N3471 = 12-0161

2012-10-18

Benjamin Kosnik, bkoz@redhat.com

Daniel Krugler, daniel.kruegler@googlemail.com

Addresses: LWG 2013

Introduction

This paper details use of the ISO C++0x constexpr feature, as initially introduced in “Generalized Constant Expressions — Revision 5” (N2235) and incorporating all other subsequent changes as per ISO C++ draft N3376. It is the first revision of N3305. Several CWG issues have also influenced the language with respect to constexpr: see issues 1099, 1125, 1194, 1195, 1197, 1198, 1199 and c++-std-core postings number 17890 and 17912. For pair and tuple, see N3140. Additional implementation experience, and subsequent changes to the core language have motivated the following changes and additions to the library specification.

Proposed Changes

A list of additional places in Chapters 18 Support or 20 Utilities that can exploit the constexpr language feature is below. For the changes listed, the new text is put in place in bold and green with the older text it is replacing struckthrough in red.

01. <initializer_list>

Add the following:

        - Mark class template initializer_list’s default constructor and member functions         

          size, begin, and end constexpr.

        - Mark function templates begin and end constexpr.

02. <utility>

Add the following:

        

        - Mark function templates forward, move, and move_if_noexcept constexpr.

        - Mark non-defaulted constructors constexpr (It is important that  

       std::pair<std::string, int>’s copy constructor is still well-formed). The intent is

       that all defaulted copy/move constructors are constexpr, if they can

       be constexpr.

        - Mark pair comparison operators ==, !=, >, >=, <, <= constexpr.

        - Mark function template make_pair constexpr

03. <tuple>

Add the following:

        - Mark non-defaulted tuple constructors constexpr (It is important that std::tuple<std::string>’ copy constructor is still well-formed). The intent is that all defaulted copy/move constructors are constexpr, if they can be constexpr.

        - Make instances of function template get constexpr

        - Mark tuple_cat and make_tuple constexpr

        - Mark tuple comparison operators ==, !=, >, >=, <, <= constexpr

Proposed wording

a) Modify 18.9 [support.initlist] as follows:

constexpr initializer_list() noexcept;

                           

constexpr size_t size() const noexcept;

                           

constexpr const E* begin() const noexcept;

                           

constexpr const E* end() const noexcept;

b) Modify 18.9.1 [support.initlist.cons] as follows:

constexpr initializer_list() noexcept;

c) Modify 18.9.2 [support.initlist.access] as follows:

constexpr const E* begin() const noexcept;

constexpr const E* end() const noexcept;

constexpr size_t size() const noexcept;

d) Modify 18.9.3 [support.initlist.range] as follows:

template<class E> constexpr const E* begin(initializer_list<E> il) noexcept;

template<class E> constexpr const E* end(initializer_list<E> il) noexcept;

e) Modify 20.2 [utility] as follows:

// 20.2.3, forward/move:

template <class T>

constexpr T&& forward(typename remove_reference<T>::type& t) noexcept;

template <class T>

constexpr T&& forward(typename remove_reference<T>::type&& t) noexcept;

template <class T>

constexpr typename remove_reference<T>::type&& move(T&&) noexcept;

template <class T>

constexpr typename conditional<

!is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value,

const T&, T&&>::type move_if_noexcept(T& x) noexcept;

….

// 20.3.3, pair specialized algorithms:

template <class T1, class T2>

constexpr bool operator==(const pair<T1,T2>&, const pair<T1,T2>&);

template <class T1, class T2>

constexpr bool operator< (const pair<T1,T2>&, const pair<T1,T2>&);

template <class T1, class T2>

constexpr bool operator!=(const pair<T1,T2>&, const pair<T1,T2>&);

template <class T1, class T2>

constexpr bool operator> (const pair<T1,T2>&, const pair<T1,T2>&);

template <class T1, class T2>

constexpr bool operator>=(const pair<T1,T2>&, const pair<T1,T2>&);

template <class T1, class T2>

constexpr bool operator<=(const pair<T1,T2>&, const pair<T1,T2>&);

template <class T1, class T2>

void swap(pair<T1,T2>& x, pair<T1,T2>& y)

template <class T1, class T2>

constexpr see below make_pair(T1&&, T2&&);

template<size_t I, class T1, class T2>

constexpr typename tuple_element<I, std::pair<T1, T2> >::type&

get(std::pair<T1, T2>&) noexcept;

template<size_t I, class T1, class T2>

constexpr typename tuple_element<I, std::pair<T1, T2> >::type&&

get(std::pair<T1, T2>&&) noexcept;

template<size_t I, class T1, class T2>

constexpr const typename tuple_element<I, std::pair<T1, T2> >::type&

get(const std::pair<T1, T2>&) noexcept;

f) Modify 20.2.3 [forward] as follows:

template <class T> constexpr  T&& forward(typename remove_reference<T>::type& t) noexcept;

template <class T> constexpr  T&& forward(typename remove_reference<T>::type&& t) noexcept;

template <class T> constexpr typename remove_reference<T>::type&& move(T&& t) noexcept;

template <class T> constexpr typename conditional<

!is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value,

const T&, T&&>::type move_if_noexcept(T& x) noexcept;

g) Modify 20.3.2 [pairs.pair] as follows:

pair(const pair&) = default;

pair(pair&&) = default;

constexpr pair();

constexpr pair(const T1& x, const T2& y);

template<class U, class V> constexpr pair(U&& x, V&& y);

template<class U, class V> constexpr pair(const pair<U, V>& p);

template<class U, class V> constexpr pair(pair<U, V>&& p);

...

and modify the following prototype declarations before paragraph 4, 6, 9, and 12 accordingly.

h) Add to 20.3.2 [pairs.pair]  after paragraph 1 as follows:

The defaulted move and copy constructor, respectively, of pair shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function.

i) Modify 20.3.3 [pairs.spec] as follows:

template <class T1, class T2>

 constexpr bool operator==(const pair<T1, T2>& x, const pair<T1, T2>& y);

template <class T1, class T2>

 constexpr bool operator<(const pair<T1, T2>& x, const pair<T1, T2>& y);

template <class T1, class T2>

 constexpr bool operator!=(const pair<T1, T2>& x, const pair<T1, T2>& y);

template <class T1, class T2>

 constexpr bool operator>(const pair<T1, T2>& x, const pair<T1, T2>& y);

template <class T1, class T2>

 constexpr bool operator>=(const pair<T1, T2>& x, const pair<T1, T2>& y);

template <class T1, class T2>

 constexpr bool operator<=(const pair<T1, T2>& x, const pair<T1, T2>& y);

template <class T1, class T2>

constexpr pair<V1, V2> make_pair(T1&& x, T2&& y)

j) Modify 20.3.4 [pair.astuple] as follows:

template<size_t I, class T1, class T2>

constexpr typename tuple_element<I, std::pair<T1, T2> >::type&

get(pair<T1, T2>&) noexcept;

template<size_t I, class T1, class T2>

constexpr const typename tuple_element<I, std::pair<T1, T2> >::type&

get(const pair<T1, T2>&) noexcept;

template<size_t I, class T1, class T2>

constexpr typename tuple_element<I, std::pair<T1, T2> >::type&&

get(pair<T1, T2>&&) noexcept;

k) Modify 20.4.1 [tuple.general] as follows:

template <size_t I, class... types>

constexpr typename tuple_element<I, tuple<Types...> >::type&

get(tuple<Types...>&) noexcept;

template <size_t I, class... types>

constexpr typename tuple_element<I, tuple<Types...> >::type &&

get(tuple<Types...>&&) noexcept;

template <size_t I, class... types>

constexpr typename tuple_element<I, tuple<Types...> >::type const&

get(const tuple<Types...>&) noexcept;

template <class... Types>

constexpr tuple<VTypes ...>

make_tuple(Types&&...);

template <class... Tuples>

constexpr tuple<Ctypes ...>

tuple_cat(Tuples&&...);

template<class... TTypes, class... UTypes>

constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator<(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator>(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator<=(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator>=(const tuple<TTypes...>&, const tuple<UTypes...>&);

l) Modify 20.4.2 [tuple.tuple] as follows:

constexpr tuple();

explicit constexpr tuple(const Types&...);

template <class... UTypes>

explicit constexpr tuple(UTypes&&...)

tuple(const tuple&) = default;

tuple(tuple&&) = default;

template <class... UTypes>

constexpr tuple(const tuple<UTypes...>&);

template <class... UTypes>

constexpr tuple(tuple<UTypes...>&&);

template <class U1, class U2>

constexpr tuple(const pair<U1, U2>&); // iff sizeof...(Types) == 2

template <class U1, class U2>

constexpr tuple(pair<U1, U2>&&); // iff sizeof...(Types) == 2

m) Modify 20.4.2.1 [tuple.cnstr] as follows:

explicit constexpr tuple(const Types&...);

template <class... UTypes>

constexpr tuple(const tuple<UTypes...>&);

template <class... UTypes>

constexpr tuple(tuple<UTypes...>&&);

template <class U1, class U2>

constexpr tuple(const pair<U1, U2>&); // iff sizeof...(Types) == 2

template <class U1, class U2>

constexpr tuple(pair<U1, U2>&&); // iff sizeof...(Types) == 2

n) Add to 20.4.2.1 [tuple.cnstr]  after paragraph 1 as follows:

 The defaulted move and copy constructor, respectively, of tuple shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function. The defaulted move and copy constructor of tuple<> shall be constexpr functions.

o) Modify 20.4.2.6 [tuple.elem] as follows:

template <size_t I, class... Types>

constexpr typename tuple_element<I, tuple<Types...> >::type & get(tuple<Types...>& t) noexcept;

template <size_t I, class... types>

constexpr typename tuple_element<I, tuple<Types...> >::type&& get(tuple<Types...>&& t) noexcept;

template <size_t I, class... Types>

constexpr typename tuple_element<I, tuple<Types...> >::type const& get(const tuple<Types...>& t) noexcept;

p) Modify 20.4.2.4 [tuple.creation] as follows:

template<class... Types>

constexpr tuple<VTypes ...>

make_tuple(Types&&... t);

template <class... Tuples>

constexpr tuple<CTypes ...>

tuple_cat(Tuples&&... tpls);

q) Modify 20.4.2.7 [tuple.rel] as follows:

template<class... TTypes, class... UTypes>

constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator<(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator>(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator<=(const tuple<TTypes...>&, const tuple<UTypes...>&);

template<class... TTypes, class... UTypes>

constexpr bool operator>=(const tuple<TTypes...>&, const tuple<UTypes...>&);

Acknowledgments

Many thanks to Daniel Krügler, Alisdair Meredith,  Jason Merrill, Paolo Carlini, and Jonathan Wakely for reviewing this document and providing assistance.