我试图将std::begin
专门用于自定义容器。我这样做是因为我想在容器中使用基于范围的for
。这就是我所拥有的:
class stackiterator { … };
class stack { … };
#include <iterator>
template <> stackiterator std::begin(stack& S)
{
return S.GetBottom();
}
我在begin
专业化的定义中遇到以下错误:
没有功能模板匹配功能模板专业化&#39;开始&#39;
我做错了什么?
答案 0 :(得分:13)
我试图将
std::begin
专门用于自定义容器。我&#39;米 这样做是因为我想在容器中使用基于范围的for
。
你正在咆哮错误的树。基于范围的for
根本不使用std::begin
。对于类类型,编译器直接查找成员begin
和end
,如果两者都找不到,则仅对关联中的空闲begin
和end
执行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()
的内容。