Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 724
emlai Avatar asked Jul 16 '15 05:07

emlai


2 Answers

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.

like image 69
T.C. Avatar answered Oct 03 '22 17:10

T.C.


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().

like image 22
R Sahu Avatar answered Oct 03 '22 17:10

R Sahu