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

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

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.

`iterator` and `const_iterator` are not required members of STL containers?

Taken from Understanding iterator/const_iterator implementation:
"although iterator and const_iterator are types declared in the scope
of vector, there is no requirement that vector(or any STL container) have a member of either
type - iterator and const_iterator are part of the interface of
std::vector e.g. overloads of the member begin() returns those types,
but nothing is said about how those function obtain the iterator they
return"
Additionally STL containers must have:
"a begin and end function that returns iterators"
The above states that iterator and const_iterator are not required members of a STL container for example vector. I assume this means that the type returned from .begin or .end will differ based on implementation.
So I am wondering why this isn't problematic as I see a lot of people write out std::vector<someType>::iterator or std::vector<someType>::const_iterator where iterator and const_iterator are specified instead of using auto for example:
for (std::vector<int>::iterator i = s.begin(); i != s.end(); i++)
{
}
You're reading the quote wrong. The person says
have a member of either type
not
have either type
When they say have a member of either type they mean there is no data member of type iterator or const_iterator in the class.
They do go on to say
iterator and const_iterator are part of the interface of std::vector
Which is correct as the standard requires that std::vector surface these types in it interface.
std::vector will normally contain a typedef (or equivalently, using) to specify the type that the name iterator will refer to. At its simplest, it might be something like:
template <class T, class Allocator>
class vector {
using iterator = T *;
using const_iterator = T const *;
// ...
};
Likewise, its begin and end must return iterators, and its cbegin and cend must return const_iterators. But, the vector object doesn't (necessarily need to contain any objects of type iterator or const_iterator.
Also note that although the names vector::iterator and vector::const_iterator need to be defined, it isn't strictly necessary that they be defined by vector itself. You could (for example) have vector derive from a base class defines the appropriate names:
template <class T>
class vector_base {
public:
using iterator = T*;
using const_iterator = T const *;
// ...
};
template <class T, class Allocator>
class vector : public vector_base<T> {
};
So, even though vector itself doesn't define iterator, the name vector::iterator is well defined. I probably wouldn't bother to mention this except for one point: although it doesn't apply to vector, there is a standard class named std::iterator, that was intended primarily as a base class for iterators, and most of what it did was pretty much what's outlined above--define the member typedefs for value_type, difference_type, reference, and so on. Its use is now deprecated, but there are still a fair number of older iterator classes that used it (and older versions of the standard at least implied that most standard iterator types should use it as well).

How to specialize std::begin?

I'm trying to specialize std::begin for a custom container. I'm doing this because I want to use range-based for with the container. This is what I have:
class stackiterator { … };
class stack { … };
#include <iterator>
template <> stackiterator std::begin(stack& S)
{
return S.GetBottom();
}
I get the following error at the definition of my begin specialization:
No function template matches function template specialization 'begin'
What am I doing wrong?
I'm trying to specialize std::begin for a custom container. I'm
doing this because I want to use range-based for with the container.
You are barking up the wrong tree. Range-based for does not use std::begin at all. For class types, the compiler looks up member begin and end directly, and if neither is found, does an ADL-only lookup for free begin and end in the associated namespaces. Ordinary unqualified lookup is not performed; there's no way for std::begin to be picked up if your class is not in the std namespace.
Even if the specialization you want to do is possible (it isn't unless you introduce a member begin() - an explicit specialization for a function template can't change the return type, and the overload at issue returns "whatever member begin() returns"; and if you do introduce a member begin(), why are you specializing std::begin to do what it would have done anyway?), you still won't be able to use it with a range-based for.
Leaving aside the policy and semantic issues of whether you should specialize a function template from the std namespace,
The following snippet does not work:
class stackiterator {};
struct stack { stackiterator Begin() { return stackiterator{};} };
#include <iterator>
namespace std
{
template <> stackiterator begin<stack>(stack& S)
{
return S.Begin();
}
}
However, the following snippet works just fine:
class stackiterator {};
struct stack { stackiterator begin() { return stackiterator{};} };
#include <iterator>
namespace std
{
template <> stackiterator begin<stack>(stack& S)
{
return S.begin();
}
}
The key difference is the presence of Begin() vs begin() as a member function of stack. std::begin() is defined as:
template <class C> auto begin(C& c) -> decltype(c.begin());
template <class C> auto begin(const C& c) -> decltype(c.begin());
When you specialize a function template, you must still keep the return type the same. When you don't have begin() as a member of Stack, the compiler does not know how to determine the return type.
That is the reason for error produced by the compiler.
BTW, there is another SO post that partially answers what can be specialized and what can't be specialized.
Looking at the part of the standard that deals with std::begin(), Section 24.3, I don't see anything about not being able to specialize std::begin().
The right way to add a free function begin that enables for(:) loops is to add, in the namespace of stack, a begin(stack&) and begin(stack const&) function that returns a iterator and const_iterator respectively (and ditto for end)
The other way is to add a member begin() and end() to stack.
Specializing std::begin is bad practice for a number of reasons, not the least of which is that not all for(:) loops will work with it (the lookup rules where changed in the resolution of this defect report). Overloading std::begin is undefined behavior (you may not overload functions in namespace std under the standard: doing so makes your program ill-formed).
This is how it has to be done, even if it violates the naming convention of your project.

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.

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)