如何专精std::begin?

How to specialize std::begin?

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

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

#include <iterator>

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

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

No function template matches function template specialization '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.

你找错人了。基于范围的 for 根本不使用 std::begin。对于 class 类型,编译器直接查找成员 beginend,如果两者都没有找到,则对 begin 和 [=15= 进行仅 ADL 查找] 在关联的命名空间中。不执行普通的不合格查找;如果您的 class 不在 std 命名空间中,则无法获取 std::begin

即使你想做的特化是可能的(除非你引入一个成员 begin() - 函数模板的显式特化不能改变 return 类型,以及有问题的超载 returns "whatever member begin() returns";如果你确实引入了一个成员 begin(),你为什么要专门化 std::begin 去做它本来会做的事情?),您仍然无法将它与基于范围的 for.

一起使用

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

另一种方法是在stack.

中添加成员begin()end()

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

必须这样做,即使它违反了项目的命名约定。

撇开是否应该从 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());

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

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

顺便说一句,another SO post 部分回答了哪些可以专门化,哪些不能专门化。

查看标准中涉及 std::begin() 的部分,第 24.3 节,我没有看到任何关于无法专门化 std::begin().

的内容