如何专门化std :: begin?

时间:2015-07-16 05:15:55

标签: c++ templates c++11 template-specialization template-function

我试图将std::begin专门用于自定义容器。我这样做是因为我想在容器中使用基于范围的for。这就是我所拥有的:

class stackiterator { … };
class stack { … };

#include <iterator>

template <> stackiterator std::begin(stack& S)
{
  return S.GetBottom();
}

我在begin专业化的定义中遇到以下错误:

  

没有功能模板匹配功能模板专业化&#39;开始&#39;

我做错了什么?

3 个答案:

答案 0 :(得分:13)

  

我试图将std::begin专门用于自定义容器。我&#39;米   这样做是因为我想在容器中使用基于范围的for

你正在咆哮错误的树。基于范围的for根本不使用std::begin。对于类类型,编译器直接查找成员beginend,如果两者都找不到,则仅对关联中的空闲beginend执行ADL查找命名空间。不执行普通的非限定查找;如果您的班级不在std::begin名称空间中,则std无法接听。begin()

即使你想要做的专业化是可能的(除非你引入一个成员begin(),否则它不是 - 一个功能模板的显式专业化不能改变返回类型,并且问题重载返回&#34;无论成员begin()返回&#34 ;;如果您确实介绍了成员std::begin,为什么要专门研究for做他们原本应该做的事情?),您仍然无法使用基于范围的{{1}}。

答案 1 :(得分:2)

添加启用begin循环的免费函数for(:)的正确方法是在stack的命名空间中添加begin(stack&)begin(stack const&)分别返回迭代器和const_iterator的函数(和end的ditto)

另一种方法是将成员begin()end()添加到stack

专业化std::begin是一种不好的做法,原因有很多,其中最重要的原因是并非所有for(:)循环都可以使用它(查找规则更改为in the resolution of this defect report)。重载std::begin是未定义的行为(您可能不会在标准下的namespace std中重载函数:这样做会使您的程序格式错误。)

即使它违反了项目的命名惯例,也必须这样做。

答案 2 :(得分:2)

不考虑是否应该专门化std命名空间中的函数模板的策略和语义问题,

以下代码段不起作用:

class stackiterator {};
struct stack { stackiterator Begin() { return stackiterator{};} };

#include <iterator>

namespace std
{
   template <> stackiterator begin<stack>(stack& S)
   {
      return S.Begin();
   }
}

但是,以下代码段工作正常:

class stackiterator {};
struct stack { stackiterator begin() { return stackiterator{};} };

#include <iterator>

namespace std
{
   template <> stackiterator begin<stack>(stack& S)
   {
      return S.begin();
   }
}

关键区别在于Begin()begin()的存在是stack的成员函数。 std::begin()定义为:

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

当您专门化函数模板时,您仍必须保持返回类型相同。当您没有begin()作为Stack的成员时,编译器不知道如何确定返回类型。

这就是编译器产生错误的原因。

顺便说一下,有another SO post部分回答了可以专业化和不专业化的内容。

查看处理std::begin()的标准部分,第24.3节,我没有看到任何关于无法专门化std::begin()的内容。