基于范围的 for 循环 - 为什么找不到这些 begin/end 函数?
Range based for loop - why are these begin/end functions not found?
考虑:
#include <iostream>
#include <vector>
class A
{
public:
typedef bool TAll;
static TAll All;
typedef std::vector<int> TVec;
static TVec m_sVec;
static TVec::iterator begin() { return m_sVec.begin(); }
static TVec::iterator end() { return m_sVec.end(); }
};
A::TVec A::m_sVec;
A::TAll A::All;
A::TVec::iterator begin(A::TAll& all) { return A::begin(); }
A::TVec::iterator end(A::TAll& all) { return A::end(); }
int _tmain(int argc, _TCHAR* argv[])
{
A::m_sVec.push_back(1);
A::m_sVec.push_back(2);
A::m_sVec.push_back(3);
for (auto a : A::All) {
//for (auto a = begin(A::All); a != end(A::All); a++) {
std::cout << a << std::endl;
}
return 0;
}
具有基于范围的 for 循环的版本(因此此代码原样)在 MSVC2013 中给我以下错误:
1><snip>: error C3312: no callable 'begin' function found for type 'A::TAll'
1><snip>: error C3312: no callable 'end' function found for type 'A::TAll'
GCC (4.8.3) 说(最后两行):
/usr/include/c++/4.8.3/initializer_list:99:5: note: template<class _Tp> constexpr cons
t _Tp* std::end(std::initializer_list<_Tp>)
end(initializer_list<_Tp> __ils) noexcept
^
/usr/include/c++/4.8.3/initializer_list:99:5: note: template argument deduction/subs
titution failed:
main.cpp:31:18: note: mismatched types 'std::initializer_list<_Tp>' and 'bool'
for (int a : A::All) {
使用函数(被注释掉的函数)的 'normal' for 循环有效(好吧,当然是在循环内部取消引用 'a' 之后);根据我对标准和 Stroustrou 的理解,它们应该是等价的。但我想不会。那么这里的问题是什么?谢谢
根据 C++11 [stmt.ranged]/1,您的循环:
for (auto a : A::All) {
std::cout << a << std::endl;
}
相当于:
{
auto && __range = (A::All);
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
auto a = *__begin;
{
std::cout << a << std::endl;
}
}
}
其中表达式 begin-expr
和 end-expr
的确定取决于初始化表达式 A::All
的类型 _RangeT
(本例中为 bool
) :
- if
_RangeT
is an array type, ...
- if
_RangeT
is a class type, ...
- otherwise, begin-expr and end-expr are
begin(__range)
and end(__range)
, respectively, where begin
and end
are looked up with argument-dependent lookup (3.4.2) [emphasis added]. For the purposes of this name lookup, namespace std
is an associated namespace.
由于 bool
既不是数组也不是 class 类型,第三点适用;表达式是 begin(__range)
和 end(__range)
,但是 begin
和 end
是使用 ADL 解析的,std
作为关联的命名空间。根据 3.4.2 [basic.lookup.argdep]/2:
For each argument type T
in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. [emphasis added] The sets of namespaces and classes are determined in the following way:
- If T is a fundamental type, its associated sets of namespaces and classes are both empty.
- ...
所以 begin
和 end
仅在 std
命名空间中查找,其中可以找到多个声明,但是 none 可以接受类型为 bool
。程序格式错误。
对于上面那些想知道重点是什么的人,请考虑以下稍微不那么抽象的例子:
#include <iostream>
#include <vector>
#include <string>
#include <xfunctional>
#include <boost/iterator/filter_iterator.hpp>
struct Employee
{
std::string name;
int age;
operator int() { return age; }
};
class Employees
{
public:
struct TOlderThan {
int m_MinimumAge;
TOlderThan& operator()(int age)
{
m_MinimumAge = age;
return *this;
}
};
static TOlderThan OlderThan;
typedef std::vector<Employee> TEmployees;
static TEmployees sEmployees;
static TEmployees::iterator begin() { return sEmployees.begin(); }
static TEmployees::iterator end() { return sEmployees.end(); }
};
Employees::TEmployees Employees::sEmployees;
Employees::TOlderThan Employees::OlderThan;
typedef boost::filter_iterator<std::binder1st<std::less<int>>, Employees::TEmployees::iterator> TFilter;
TFilter begin(const Employees::TOlderThan& min_age) { return boost::make_filter_iterator(std::bind1st(std::less<int>(), min_age.m_MinimumAge), Employees::begin(), Employees::end()); }
TFilter end(const Employees::TOlderThan& min_age) { return boost::make_filter_iterator(std::bind1st(std::less<int>(), min_age.m_MinimumAge), Employees::end(), Employees::end()); }
int main(int argc, _char* argv[])
{
Employees::sEmployees.push_back({"John", 34});
Employees::sEmployees.push_back({"Pete", 48});
Employees::sEmployees.push_back({"Jake", 59});
for (Employee& e : Employees::OlderThan(40)) {
std::cout << e.name << std::endl;
}
return 0;
}
输出,如预期
Pete
Jake
基本上,此功能允许您用不到 10 行代码将几乎 DSL 风格的行为构建到您的 API 中。很帅
考虑:
#include <iostream>
#include <vector>
class A
{
public:
typedef bool TAll;
static TAll All;
typedef std::vector<int> TVec;
static TVec m_sVec;
static TVec::iterator begin() { return m_sVec.begin(); }
static TVec::iterator end() { return m_sVec.end(); }
};
A::TVec A::m_sVec;
A::TAll A::All;
A::TVec::iterator begin(A::TAll& all) { return A::begin(); }
A::TVec::iterator end(A::TAll& all) { return A::end(); }
int _tmain(int argc, _TCHAR* argv[])
{
A::m_sVec.push_back(1);
A::m_sVec.push_back(2);
A::m_sVec.push_back(3);
for (auto a : A::All) {
//for (auto a = begin(A::All); a != end(A::All); a++) {
std::cout << a << std::endl;
}
return 0;
}
具有基于范围的 for 循环的版本(因此此代码原样)在 MSVC2013 中给我以下错误:
1><snip>: error C3312: no callable 'begin' function found for type 'A::TAll'
1><snip>: error C3312: no callable 'end' function found for type 'A::TAll'
GCC (4.8.3) 说(最后两行):
/usr/include/c++/4.8.3/initializer_list:99:5: note: template<class _Tp> constexpr cons
t _Tp* std::end(std::initializer_list<_Tp>)
end(initializer_list<_Tp> __ils) noexcept
^
/usr/include/c++/4.8.3/initializer_list:99:5: note: template argument deduction/subs
titution failed:
main.cpp:31:18: note: mismatched types 'std::initializer_list<_Tp>' and 'bool'
for (int a : A::All) {
使用函数(被注释掉的函数)的 'normal' for 循环有效(好吧,当然是在循环内部取消引用 'a' 之后);根据我对标准和 Stroustrou 的理解,它们应该是等价的。但我想不会。那么这里的问题是什么?谢谢
根据 C++11 [stmt.ranged]/1,您的循环:
for (auto a : A::All) {
std::cout << a << std::endl;
}
相当于:
{
auto && __range = (A::All);
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
auto a = *__begin;
{
std::cout << a << std::endl;
}
}
}
其中表达式 begin-expr
和 end-expr
的确定取决于初始化表达式 A::All
的类型 _RangeT
(本例中为 bool
) :
- if
_RangeT
is an array type, ...- if
_RangeT
is a class type, ...- otherwise, begin-expr and end-expr are
begin(__range)
andend(__range)
, respectively, wherebegin
andend
are looked up with argument-dependent lookup (3.4.2) [emphasis added]. For the purposes of this name lookup, namespacestd
is an associated namespace.
由于 bool
既不是数组也不是 class 类型,第三点适用;表达式是 begin(__range)
和 end(__range)
,但是 begin
和 end
是使用 ADL 解析的,std
作为关联的命名空间。根据 3.4.2 [basic.lookup.argdep]/2:
For each argument type
T
in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. [emphasis added] The sets of namespaces and classes are determined in the following way:
- If T is a fundamental type, its associated sets of namespaces and classes are both empty.
- ...
所以 begin
和 end
仅在 std
命名空间中查找,其中可以找到多个声明,但是 none 可以接受类型为 bool
。程序格式错误。
对于上面那些想知道重点是什么的人,请考虑以下稍微不那么抽象的例子:
#include <iostream>
#include <vector>
#include <string>
#include <xfunctional>
#include <boost/iterator/filter_iterator.hpp>
struct Employee
{
std::string name;
int age;
operator int() { return age; }
};
class Employees
{
public:
struct TOlderThan {
int m_MinimumAge;
TOlderThan& operator()(int age)
{
m_MinimumAge = age;
return *this;
}
};
static TOlderThan OlderThan;
typedef std::vector<Employee> TEmployees;
static TEmployees sEmployees;
static TEmployees::iterator begin() { return sEmployees.begin(); }
static TEmployees::iterator end() { return sEmployees.end(); }
};
Employees::TEmployees Employees::sEmployees;
Employees::TOlderThan Employees::OlderThan;
typedef boost::filter_iterator<std::binder1st<std::less<int>>, Employees::TEmployees::iterator> TFilter;
TFilter begin(const Employees::TOlderThan& min_age) { return boost::make_filter_iterator(std::bind1st(std::less<int>(), min_age.m_MinimumAge), Employees::begin(), Employees::end()); }
TFilter end(const Employees::TOlderThan& min_age) { return boost::make_filter_iterator(std::bind1st(std::less<int>(), min_age.m_MinimumAge), Employees::end(), Employees::end()); }
int main(int argc, _char* argv[])
{
Employees::sEmployees.push_back({"John", 34});
Employees::sEmployees.push_back({"Pete", 48});
Employees::sEmployees.push_back({"Jake", 59});
for (Employee& e : Employees::OlderThan(40)) {
std::cout << e.name << std::endl;
}
return 0;
}
输出,如预期
Pete
Jake
基本上,此功能允许您用不到 10 行代码将几乎 DSL 风格的行为构建到您的 API 中。很帅