According to cppreference, std::cbegin() for containers was introduced in C++14 as constexpr - but std::begin(), introduced in C++11, remained non-constexpr until C++17.
Really? That seems very lopsided. What was the reason for this?
The overload
template< class T, std::size_t N >
constexpr T* begin( T (&array)[N] ) noexcept;
was constexpr in C++14, so std::cbegin, which calls std::begin, was also made constexpr in C++14.
For non-arrays, std::cbegin was not usable in constant expressions in C++14, even though the function template is still marked constexpr.
Related
Recently i was viewing std::array header file, and i've found some wierd defines, such as _GLIBCXX20_CONSTEXPR or _GLIBCXX17_CONSTEXPR etc. Why are they needed?
_GLIBCXX20_CONSTEXPR expands to constexpr when using C++20 or newer, and to nothing otherwise.
_GLIBCXX17_CONSTEXPR is similar, but for C++17.
These macros are empty if a compiler does not enable support of C++17 or C++20 respectively. The different macros are required since C++20 adds more constexpr API after C++17.
The similar macro _GLIBCXX14_CONSTEXPR exists. C++17 adds more constexpr API after C++14.
There is no constexpr API prior C++11.
Example std::array<T,N>::at, C++ Containers library std::array
reference at( size_type pos ); (until C++17)
constexpr reference at( size_type pos ); (since C++17)
That looks in the header file like
_GLIBCXX17_CONSTEXPR reference at( size_type pos );
As per the documentation(https://en.cppreference.com/w/cpp/utility/move), there are two kinds of constructors for std::move<T>, which are posted below.
What are the differences between these constructors?
What confused me most is that why there needs the keyword(typename) in the second constructor.
I am a novice in C++. I would be thankful for any hint on this question.
template< class T >
typename std::remove_reference<T>::type&& move( T&& t ) noexcept; (since C++11)(until C++14)
template< class T >
constexpr typename std::remove_reference<T>::type&& move( T&& t ) noexcept; (since C++14)
[...] there are two kinds of constructors for std::move<T>...
No, they are not constructors, rather function signatures of std::move. One is prior to c++14(i.e. since c++11) and the second one since C++14.
In the second one the specifier constexpr is used, meaning
constexpr - specifies that the value of a variable or function can
appear in constant expressions
read more here:What are 'constexpr' useful for?
What confused me most is that why there needs the keyword(typename) in
the second constructor.
As per the cppreference.com, there is a helper type for std::remove_reference, since c++14
template< class T >
using remove_reference_t = typename remove_reference<T>::type; (since C++14)
therefore in the second one, it could have been
template< class T >
constexpr std::remove_reference_t<T>&& move( T&& t ) noexcept;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
From cppreference:
template< class M, class N>
constexpr std::common_type_t<M, N> gcd(M m, N n); (since C++17)
IIUC, to return right type,
template< class M, class N>
constexpr auto gcd(M m, N n); (since C++17)
has the same effects and more elegent since c++14. std::common_type_t is just a workaround for c++11. But std::gcd is since c++17
auto indicates that the type should be inferred from the implementation. Putting auto on a documentation page without the implementation is not helpful to anyone reading the docs. On the other hand, std::common_type_t has well-defined behavior which is linked to straight from that documentation page, making it much more helpful.
Specifying that std::gcd returns "auto" wouldn't tell the implementers or the users of the standard API anything about what the function returns. Such specification wouldn't be useful.
Note that although the return type is specified to be std::common_type_t<M, N>, nothing prevents an implementation from using auto return type in their header, as long as the deduced type conforms to the specification.
std::common_type_t is just a workaround for c++11
No. std::common_type_t alias for std::common_type::type was introduced in C++14. And it's not a "workaround".
C++11 introduced std::begin() non-member function without constexpr-specifier, and then C++14 updates to constexpr-std::begin() for array type(T (&)[N]) and appends constexpr-std::cbegin() for generic container type(const C&).
Quote from http://en.cppreference.com/w/cpp/iterator/begin
template< class T, size_t N >
constexpr T* begin( T (&array)[N] ); // (since C++14)
template< class C >
constexpr auto cbegin( const C& c ) -> decltype(std::begin(c)); // (since C++14)
So we can use std::begin() and/or std::cbegin() in constexpr context for raw array type T[N] (for C++14 constexpr function).
Question:
C++14 does NOT allow non-member std::begin() in constexpr context for "Standard Containers" such as std::array, because they doesn't provide constexpr-begin() member function. Is my interpretation correct?
Why does non-member std::cbegin() have constexpr-specifier? For user provided container which have constexpr-begin() member function?
Current constexpr support in the Standard Library is indeed rather limited.
non-member std::begin is not marked constexpr because apart from array and initializer-list, no Standard Container (or container like entity such as bitset) supports constexpr member begin() (mainly because some implementations want to use iterator debugging using dynamic memory allocation). Your interpretation here is correct.
non-member std::cbegin is marked constexpr in order to support the (currently) two constexpr std::begin non-member functions for array and initializer_list, and to be forward-compatible for future upgrades in the Standard Library.
Regarding point 2., it is not so useful for user-defined container like entities, because there the accepted idiom is to define non-member begin() and end() in the namespace surrounding the user-defined type, and not in namespace std.
In C++11 (quoting N3337), std::begin() and std::end() are specified as (§24.7 [iterator.range]/p2-3)
template <class C> auto begin(C& c) -> decltype(c.begin());
template <class C> auto begin(const C& c) -> decltype(c.begin());
2 Returns: c.begin().
template <class C> auto end(C& c) -> decltype(c.end());
template <class C> auto end(const C& c) -> decltype(c.end());
3 Returns: c.end().
std::initializer_list, however, provides its own overloads for these functions (§18.9.3 [support.initlist.range]):
template<class E> const E* begin(initializer_list<E> il) noexcept;
1 Returns: il.begin().
template<class E> const E* end(initializer_list<E> il) noexcept;
2 Returns: il.end().
It seems that these overloads don't do anything beyond the base template, other than (1) having a noexcept specification and (2) taking their parameter by value. However, copying an initializer_list does nothing special (it just copies a pair of pointers or something equally lightweight), so (2) creates no difference in behavior. Moreover, the begin() and end() member functions of many standard containers are also noexcept, yet no overloads of std::begin()/std::end() are specified for those containers, so it seems unlikely the committee specified these overloads just for (1). Why, then, are these overloads provided?
This is explained in N2930, which proposed the change to add the overloads in question.
Emphasis mine:
Summary of proposed changes
Specify range-based for statements without the use of concepts, using argument-dependent lookup of begin and end (which always includes those begin and end functions in namespace std) to provide the iterator to the beginning and ending of the sequence.
Specify range-based for statements so that arrays and initializer lists have no dependencies on <iterator>.
Refactor <initializer_list> so that it as no dependencies on other headers and includes no other headers.
Specify the library headers that are to #include <initializer_list>.
It seems like they didn't explain why they wanted <initializer_list> to have no dependency on <iterator>, but I think a reasonable guess is that the former is supposed to be available in a freestanding implementation whereas the latter might not be (Table 16, §17.6.1.3)