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

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.

Related

Difference between std::vector::empty and std::empty

To check if a vector v is empty, I can use std::empty(v) or v.empty(). I looked at the signatures on cppreference, but am lacking the knowledge to make sense of them. How do they relate to each other? Does one implementation call the other?
I know that one comes from the containers library and the other from the iterators library, but that is about it.
Difference between std::vector::empty and std::empty
The difference between Container::empty member function and std::empty free function (template) is the same as difference between Container::size,std::size, Container::data,std::data, Container::begin,std::begin and Container::end,std::end.
In all of these cases for any standard container, the free function (such as std::empty) simply calls the corresponding member function. The purpose for the existence of the free function is to provide a uniform interface between containers (and also std::initializer_list) and arrays. Arrays cannot have member functions like the class templates can have, so they have specialised overload for these free functions.
If you are writing code with a templated container type, then you should be using the free function in order to be able to support array as the templated type. If the type isn't templated, then there is no difference between the choice of using member function or the free function other than the convenience of potential refactoring into a template (or just plain array).
There are three overloads of std::empty, but the one used by std::empty(v) for a vector v is the first:
template <class C>
constexpr auto empty(const C& c) -> decltype(c.empty()); // (since c++17, until c++20)
template <class C>
[[nodiscard]] constexpr auto empty(const C& c) -> decltype(c.empty());
(since C++20) // (since c++20)
This overload has the following effect:
returns c.empty()
So, std::empty(v) and v.empty() have the same effect in this case.
std::empty returns the result of calling std::vector::empty.
std::empty is useful for scenarios where a container may or may not provide a member function empty() for types providing a member function empty, std::empty provides a default implementation, but for a custom type not providing this function you can provide a function empty at namespace scope for use in templates; thanks to argument dependent lookup the function in the same namespace as the parameter will be used as fallback:
namespace Custom
{
struct Container
{
bool m_empty;
};
constexpr bool empty(Container const& c) // custom implementation for our own type
{
return c.m_empty;
}
}
template<class T>
void PrintEmpty(char const* containerName, T&& container)
{
using namespace std;
std::cout << containerName << " is " << (empty(container) ? "empty" : "not empty") << '\n';
}
int main()
{
PrintEmpty("std::vector<int>()", std::vector<int>());
PrintEmpty("Container{}", Custom::Container{});
PrintEmpty("Container{ true }", Custom::Container{ true });
}
To language-lawyer a bit, the C++20 standard says that a container a has its a.empty() member function return true if and only if a.begin() == a.end() ([container.requirements.general]).
The std::empty() non-member function is specified in [iterator.range]. The overload constexpr auto empty(const C& c) “Returns: c.empty().”
So, in the Standard, the non-member function is specified in terms of the member functions, not vice versa. In general, that doesn’t mean that the implementation needs to have the non-member function actually call the member function, so long as it “behaves as if.” In this case, the relationship must hold for any allowed specialization of the STL container classes, or for custom container classes, so that constrains what the writers of the library are allowed to do.

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.

Can I use the iterator Libraries' Access Functions on Nonstandard Containers?

The iterator library has been introducing a lot of access functions over the course of C++11, C++14, and C++17:
begin/end
cbegin/cend
crbegin/crend
data
empty
rbegin/rend
size
Can I use these on any container, even nonstandard containers (provided they supply an accessible corresponding method?) For example given a QVector foo can I do this:
const auto bar = begin(foo);
The declarations for std::begin are as follow (from §24.7):
template <class C> auto begin(C& c) -> decltype(c.begin());
template <class C> auto begin(const C& c) -> decltype(c.begin());
So these functions will be defined for any class C such that c.begin() is "valid" (does exist). The standard also guarantees that these will:
Returns: c.begin().
So yes you can use begin(c) on any container of type C as long as either:
The member function C::begin() is provided.
There exist a begin(C const&) or begin(C &) function.
The standalone begin function should not be in the std:: namespace but rather in the same namespace as your class C so name-lookup can find it.
As I understand it, your question seem to be: if the container provides a member function begin, can I then call it as a free function? The answer to your question is yes, because a templated free function begin is provided by the standard, that simply tries to call the member function begin; from http://en.cppreference.com/w/cpp/iterator/begin: "Returns an iterator to the beginning of the given container c or array array. These templates rely on C::begin() having a reasonable implementation.".

SFINAE-ing any container into a c-style array view

I'm making a simple, non-owning array view class:
template <typename T>
class array_view {
T* data_;
size_t len_;
// ...
};
I want to construct it from any container that has data() and size() member functions, but SFINAE-d correctly such that array_view is only constructible from some container C if it would then be valid and safe behavior to actually traverse data_.
I went with:
template <typename C,
typename D = decltype(std::declval<C>().data()),
typename = std::enable_if_t<
std::is_convertible<D, T*>::value &&
std::is_same<std::remove_cv_t<T>,
std::remove_cv_t<std::remove_pointer_t<D>>>::value>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
That seems wholly unsatisfying and I'm not even sure it's correct. Am I correctly including all the right containers and excluding all the wrong ones? Is there an easier way to write this requirement?
If we take a look at the proposed std::experimental::array_view in N4512, we find the following Viewable requirement in Table 104:
Expression Return type Operational semantics
v.size() Convertible to ptrdiff_t
v.data() Type T* such that T* is static_cast(v.data()) points to a
implicitly convertible to U*, contiguous sequence of at least
and is_same_v<remove_cv_t<T>, v.size() objects of (possibly
remove_cv_t<U>> is true. cv-qualified) type remove_cv_t<U>.
That is, the authors are using essentially the same check for .data(), but add another one for .size().
In order to use pointer arithmetic on U by using operations with T, the types need to be similar according to [expr.add]p6. Similarity is defined for qualification conversions, this is why checking for implicit convertibility and then checking similarity (via the is_same) is sufficient for pointer arithmetic.
Of course, there's no guarantee for the operational semantics.
In the Standard Library, the only contiguous containers are std::array and std::vector. There's also std::basic_string which has a .data() member, but std::initializer_list does not, despite it being contiguous.
All of the .data() member functions are specified for each individual class, but they all return an actual pointer (no iterator, no proxy).
This means that checking for the existence of .data() is currently sufficient for Standard Library containers; you'd want to add a check for convertibility to make array_view less greedy (e.g. array_view<int> rejecting some char* data()).
The implementation can of course be moved away from the interface; you could use Concepts, a concepts emulation, or simply enable_if with an appropriate type function. E.g.
template<typename T, typename As,
typename size_rt = decltype(std::declval<T>().size())
typename data_rt = decltype(std::declval<T>().data())>
constexpr bool is_viewable =
std::is_convertible_v<size_rt, std::ptrdiff_t>
&& std::is_convertible_v<data_rt, T*>
&& std::is_same_v<std::remove_cv_t<T>, std::remove_cv_t<data_rt>>;
template <typename C,
typename = std::enable_if_t<is_viewable<C, T>>
>
array_view(C&& container)
: data_(container.data()), len_(container.size())
{ }
And yes, that doesn't follow the usual technique for a type function, but it is shorter and you get the idea.

Why are std::begin() and std::end() overloaded for std::initializer_list in C++11?

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)