What are defines in STL mean? - c++

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 );

Related

std::span constructor, libcxx vs libstdc++, template vs non-template?

Strolling through the code of libcxx's std::span, I noticed that the first two value constructors (number 2 and 3 on cppreference) were not templates.
_LIBCPP_INLINE_VISIBILITY constexpr span(pointer __ptr, index_type __count) : __data{__ptr}
{ (void)__count; _LIBCPP_ASSERT(_Extent == __count, "size mismatch in span's constructor (ptr, len)"); }
_LIBCPP_INLINE_VISIBILITY constexpr span(pointer __f, pointer __l) : __data{__f}
{ (void)__l; _LIBCPP_ASSERT(_Extent == distance(__f, __l), "size mismatch in span's constructor (ptr, ptr)"); }
Instead of the template types It and End as shown on the cppreference page, they use the pointer type directly. So I was wondering whether or not the libcxx code is conforming.
I wanted a point of comparison, so I went ahead and looked at the libstdc++ version, and this one does use templates (and is quite a bit longer as a result).
template<contiguous_iterator _It>
requires __is_compatible_ref<iter_reference_t<_It>>::value
constexpr explicit(extent != dynamic_extent)
span(_It __first, size_type __count)
noexcept
: _M_extent(__count), _M_ptr(std::to_address(__first))
{
if constexpr (_Extent != dynamic_extent)
{
__glibcxx_assert(__count == _Extent);
}
}
template<contiguous_iterator _It, sized_sentinel_for<_It> _End>
requires __is_compatible_ref<iter_reference_t<_It>>::value
&& (!is_convertible_v<_End, size_type>)
constexpr explicit(extent != dynamic_extent)
span(_It __first, _End __last)
noexcept(noexcept(__last - __first))
: _M_extent(static_cast<size_type>(__last - __first)),
_M_ptr(std::to_address(__first))
{
if constexpr (_Extent != dynamic_extent)
{
__glibcxx_assert((__last - __first) == _Extent);
}
}
Now, on the cppreference page, it is mentioned that these two constructors only participate in overload resolution if It satisfies contiguous_iterator and if the conversion from std::iter_reference_t<It> to element_type is at most a qualification conversion. To satisfy that, the libstdc++ code uses the contiguous_iterator concept as template typename (defined here) and the __is_compatible_ref requirement (defined just above).
So, here is my question: is using the pointer type directly, instead of messing with concepts and requirements, actually conforming to the standard? Is the libcxx code correct and the libstdc++ code just overcomplicated?
I would also like to extend the question to the next three constructors (dealing with raw arrays and std::array, number 4, 5, and 6 on cppreference).
The iterator-constructor for std::span you mention was proposed in P1394R4. According to libc++ C++2a Status, this paper has not yet been implemented in libc++.
libc++ implements P0122R7, where there is that pointer-constructor (replaced by the iterator-constructor in P1394R4).
P1872R0 is also related: span should have size_type, not index_type.
is using the pointer type directly, ... actually conforming to the standard?
No, such constructor is not sufficient to provide the template iterator constructor. The quoted / linked libc++ implementation does not conform to the upcoming standard.
libstdc++ template constructor is conforming.

Was begin() made constexpr in C++17 but cbegin() in C++14?

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.

Why std::gcd/lcm returns std::common_type_t<M, N> instead of auto?

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".

non-member function begin()/cbegin() and its constexpr-ness

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.

std::back_inserter needs const_reference on older GCC. Why?

I am currently looking at some code that can be compiled on newer versions of GCC but not on older ones. In my case I am using a std::back_inserter to std::copy some data from one data structure to a custom data structure. If I forget the typedef value_type & const_reference typedef in this custom data structure however, this will not compile on GCC 4.4. The same code compiles and runs just fine on GCC 4.5.
What is the difference in between these two compiler versions, that makes the code compile on one version but not on the other. I would guess it has something to do with the implementation of C++11 which was much less complete in GCC 4.4. Probably something with decltype or another new C++11 keyword, I would guess.
Also is this code correct, if I use the std::back_inserter without defining the const_reference type? I usually thought that one has to implement the full set of typedefs (value_type, reference, const_reference etc) in order to be compatible with the STL-algorithms library? Or can I safely assume that if my code compiles in this case I am not invoking anything dangerous (e.g. move semantics, which would destroy my other datastructure).
The standard (1998) says that std::back_insert_iterator needs Container::const_reference. In "24.4.2.1 Template class back_insert_iterator", [lib.back.insert.iterator], it says:
back_insert_iterator<Container>&
operator=(typename Container::const_reference value);
The 2011 standard only wants Container::value_type,
back_insert_iterator<Container>&
operator=(const typename Container::value_type& value);
back_insert_iterator<Container>&
operator=(typename Container::value_type&& value);
So, to be compatible with both versions of the C++ standard, define both value_type and const_reference_type.
In both GCC 4.4.6 and 4.5.1, the definition of operator= is identical (libstdc++-v3/include/bits/stl_iterator.h):
back_insert_iterator&
operator=(typename _Container::const_reference __value)
{
container->push_back(__value);
return *this;
}
and I get the same error with both compilers, perhaps you'll need to double check if you're using the correct compiler versions.
The reason that you need const_reference defined for your data-structure is because the assignment operator in GCC 4.4 for an lvalue argument type in the std::back_insert_iterator class is defined as:
template<class Container>
back_insert_iterator<Container>&
back_insert_iterator<Container>::operator=
(typename Container::const_reference value);
Thus const_reference needs to be a resolvable identifier in your class-type in to properly instantiate the assignment operator in the std::back_insert_iterator class template.
In GCC 4.5, this definition of the assignment operator for lvalue arguments has been changed to
template<class Container>
back_insert_iterator<Container>&
back_insert_iterator<Container>::operator=
(const typename Container::value_type& value);
in order to support the new C++11 specification. Since your code compiles correctly with GCC 4.5, I'm assuming you must have value_type properly defined for your data-structure.