10

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

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

#include <iterator>

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

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

没有函数模板与函数模板特化“开始”匹配

我究竟做错了什么?

4

3 回答 3

14

我正在尝试专门std::begin针对自定义容器。我这样做是因为我想for在容器中使用基于范围的。

你在叫错树。基于范围的for根本不使用std::begin。对于类类型,编译器直接查找成员beginend如果两者都找不到,则在关联的命名空间中免费begin进行仅 ADL 查找。end不进行普通的不合格查找;std::begin如果您的类不在命名空间中,则无法获取std

即使您想要做的特化是可能的(除非您引入一个成员begin()- 函数模板的显式特化不能更改返回类型,并且有问题的重载返回“无论成员begin()返回什么”;并且如果你确实介绍了一个成员begin(),你为什么要专门std::begin做它本来会做的事情?),你仍然无法将它与基于范围的for.

于 2015-07-16T05:41:35.663 回答
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()vsbegin()作为 的成员函数的存在stackstd::begin()定义为:

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

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

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

顺便说一句,还有另一个 SO 帖子部分回答了什么可以专门化,什么不能专门化。

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

于 2015-07-17T18:24:36.877 回答
1

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

另一种方法是添加成员begin()并添加end()stack.

由于多种原因,专业化std::begin是不好的做法,其中最重要的是并非所有for(:)循环都可以使用它(在此缺陷报告的解决方案中更改了查找规则)。重载std::begin是未定义的行为(您可能不会namespace std在标准下重载函数:这样做会使您的程序格式错误)。

这就是它必须完成的方式,即使它违反了项目的命名约定。

于 2015-07-16T14:28:10.110 回答