When to use std::begin and std::end instead of container specific versions [duplicate]
Asked Answered
B

3

112

Are there any general preferences or rules that explain when container specific versions of begin and end should be used instead of free functions std::begin and std::end?

It is my understanding that if the function is a template whereby the container type is a template parameter then std::begin and std::end should be used, i.e.:

template<class T> void do_stuff( const T& t )
{
    std::for_each( std::begin(t), std::end(t), /* some stuff */ );
}

What about in other scenarios such as a standard / member function where the type of container is known? Is it still better practice to use std::begin(cont) and std::end(cont) or should the container's member functions cont.begin() and cont.end() be preferred?

Am I correct in assuming that there is no benefit in performance by calling cont.end() over std::end(cont)?

Bloodandthunder answered 9/12, 2011 at 21:49 Comment(3)
Personally - I intend to always use begin(x) over x.begin(). It's more adaptable (I can overload begin() to do the right thing - just as it is overloaded for arrays). Although keyword 'auto' fixes part of the problem of having to spell out the types constantly, begin() and end() finish it off nicely so that everything is derived by the compiler from the arguments themselves. Much slicker.Former
Most generic use is probably: using std::begin; begin(c); to let ADL do its work.Barling
@user7116 I flagged the other one as a duplicate of this one - this one seems to be better described, and seems to have more elaborate answers. Or maybe they can be merged?Shaduf
R
44

If you look at, say, the definition of std::begin:

template< class C > 
auto begin( C& c ) -> decltype(c.begin());

You see that all it does is reference the begin() anyway. I suppose a decent compiler will make the difference nil, so I guess it comes down to preference. Personally, I'd use cont.begin() and cont.end() just so that I wouldn't have to explain it to anybody :)

As Mooing Duck points out, however, std::begin also works on arrays:

template< class T, size_t N > 
T* begin( T (&array)[N] );

... so there is that to consider. If you are not using arrays, I'd go with my suggestion. However if you are unsure if what is passed is going to be an STL container, or an array of <T>, then std::begin() is the way to go.

Recaption answered 9/12, 2011 at 21:55 Comment(5)
As I think on it, it could also be handy to specialize std::begin for c-structs. Your amendment handles this though, so +1.Tindle
The fact that it works on arrays is the reason I would always use std::begin from template.Bloodandthunder
[Dig] Not only does it work just on arrays, but using std::begin() enables the caller to specialise it if required for e.g. a type that doesn't have the usual begin/end functions, and to which they cannot be added. I'd presume that even better than this would be using std::begin; auto beg = begin(c); In the case where a specialisation of std::begin() is insufficient - e.g. partial specialisation - this will support argument-dependent lookup of begin().Auston
I've downvoted because I think this is the wrong answer. It doesn't come down to preference; std::begin simply allows more flexibility for the caller, and not just for arrays.Publicness
I think, you don't have to spell out using std::begin, the argument dependent lookup will do it automatically, won't it? It does for me.Trawick
A
86

The free function version is more generic than the member function of the container. I would use it probably in generic code where the type of the container is not known before hand (and might be an array). In the rest of the code (i.e. when the container is fixed and known) I would probably use c.begin() due to inertia. I would expect new text books on C++ to recommend the free function version (as it is never worse and sometimes better), but that has to catch up with common usage.

Apperceive answered 9/12, 2011 at 23:17 Comment(10)
+1 for mentioning it would be 'due to inertia'Dvinsk
Not inertia only, but also autocomplete and few extra letters for std:: if you (or your style guide) avoid ADL. From my practice, places where free begin/end are truly needed are exceedingly rare.Daily
You shouldn't avoid ADL for no good reason. Besides, range-based for is defined in terms of begin and end looked up via ADL.Appel
@Joe: That is half of the truth. The begin and end subexpressions are looked up in a three step process, of which ADL is only the last one if the other two did not apply. ADL is great and needed in some contexts, but lookup for unqualified identifiers might yield unexpected results and there are code conventions that aim to avoid it where not necessary.Wilterdink
@DavidRodríguez-dribeas No, the three step process applies to regular name lookups, not to the lookup in a range based for loop which explicitly skips unqualified lookup (the standard as published before the DR even directly says that ADL is used. See DR1442 ). As the names are not qualified by definition in this case, only ADL remains.Appel
@Joe: C++11 standard, 6.5.4 contains three bullets identifying how the lookup for the begin-expr and end-expr is done. None of which is unqualified-lookup. If you read carefully the DR you linked you will notice that it starts with otherwise. The change is to the last bullet of the three. The three bullets are: if array, resolve in place (not using std::begin/end), otherwise member lookup trying to find .begin()/end(); if both fail do a fancy ADL lookup with the addition of ::std as an associated namespace.Wilterdink
@DavidRodríguez-dribeas Aside from the fact that a special case for arrays isn't exactly a "step", the standard (draft) I see online (n3242) only has TWO bullet points, of which the first is the array special case.Appel
@Joe: The closest to the C++11 standard is n3337, which contains the three bullet points I mentioned. The text remains unmodified in n3797 which is the last openly available draft for C++14 (the following draft, n3936, is restricted for committee members)Wilterdink
@DavidRodríguez-dribeas odd, open-std.org/JTC1/SC22/WG21 only links to n3242 for C++11 and not n3337, are you certain that n3337 is C++11 and not some intermediate draft? (especially considering that it was released in 2012)Appel
@Joe: You can google for it, I believe there is even an question in SO regarding this (#17227193). n3337 is the first draft after C++11, with only editorial changes over the approved standard. Wikipedia: en.wikipedia.org/wiki/C%2B%2B11Wilterdink
R
44

If you look at, say, the definition of std::begin:

template< class C > 
auto begin( C& c ) -> decltype(c.begin());

You see that all it does is reference the begin() anyway. I suppose a decent compiler will make the difference nil, so I guess it comes down to preference. Personally, I'd use cont.begin() and cont.end() just so that I wouldn't have to explain it to anybody :)

As Mooing Duck points out, however, std::begin also works on arrays:

template< class T, size_t N > 
T* begin( T (&array)[N] );

... so there is that to consider. If you are not using arrays, I'd go with my suggestion. However if you are unsure if what is passed is going to be an STL container, or an array of <T>, then std::begin() is the way to go.

Recaption answered 9/12, 2011 at 21:55 Comment(5)
As I think on it, it could also be handy to specialize std::begin for c-structs. Your amendment handles this though, so +1.Tindle
The fact that it works on arrays is the reason I would always use std::begin from template.Bloodandthunder
[Dig] Not only does it work just on arrays, but using std::begin() enables the caller to specialise it if required for e.g. a type that doesn't have the usual begin/end functions, and to which they cannot be added. I'd presume that even better than this would be using std::begin; auto beg = begin(c); In the case where a specialisation of std::begin() is insufficient - e.g. partial specialisation - this will support argument-dependent lookup of begin().Auston
I've downvoted because I think this is the wrong answer. It doesn't come down to preference; std::begin simply allows more flexibility for the caller, and not just for arrays.Publicness
I think, you don't have to spell out using std::begin, the argument dependent lookup will do it automatically, won't it? It does for me.Trawick
D
12

Barring some optimisations being turned off to debug, there won't be a performance benefit to using cont.begin() (or getting a pointer to the first element, or whatever) unless someone's provided a really weird implementation! Pretty much all implementations (and certainly those with STL) are wafer-thin and melt in the compiler's mouth.

The plus-side is in the "or whatever" above: The same code works across different collection types whether another from the STL, or arrays, or some bizarre collection by a third party if they thought to supply a specialisation of begin for it. Even if you never use that, begin() is well-known enough that there should be a familiarity benefit.

Dietrich answered 9/12, 2011 at 22:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.