为什么范围解析不超出 class?
Why doesn't the scope resolution not go outside of the class?
我正在尝试为使用双向迭代器的容器编写一个通用的反向包装器 std::reverse_iterator
。
但是,当编译器查找 begin(...)
或 end(...)
时,它会说找不到对 reverse_wrapper<CONTAINER>::begin(container)
的匹配函数调用,因为候选需要 0 个参数,但提供了 1 个。
我猜这是因为 std::begin(myArray&)
和 std::end(myArray&)
不存在。强制他们进入 std
命名空间是行不通的(无论如何都是不可取的)。我还尝试从我的 reverse_wrapper
中删除 std::
前缀,但这没有用,而且还会破坏正在工作的 std
容器实现。
这似乎是范围解析问题,但我似乎无法解决。我做错了什么?
代码:
#include <iterator>
#include <iostream>
#include <vector>
#define Fn1 0 // std container WORKS
#define Fn2 1 // non-std container DOESN'T WORK
template <typename Container>
struct reverse_wrapper
{
Container& container;
auto begin()
-> std::reverse_iterator< decltype(std::end(container)) >
{
return std::reverse_iterator< decltype(std::end(container)) >(std::end(container));
}
auto end()
-> std::reverse_iterator< decltype(std::begin(container)) >
{
return std::reverse_iterator< decltype(std::begin(container)) >(std::begin(container));
}
};
template <typename Container>
auto reverse(Container&& container)
-> reverse_wrapper<Container>
{
return{ container };
}
struct myArray
{
int elements[5] = {1,2,3,4,5};
};
int* begin(myArray& array) { return &array.elements[0]; }
int* end(myArray& array) { return &array.elements[5]; }
#if Fn1
void fn1()
{
std::vector<int> x = { 1,2,3,4,5 };
for (auto& ix : reverse(x))
{
std::cout << ix << std::endl;
}
std::cout << "-----" << std::endl;
for (auto& ix : x)
{
std::cout << ix << std::endl;
}
}
#endif
#if Fn2
void fn2()
{
myArray x;
for (auto& ix : reverse(x))
{
std::cout << ix << std::endl;
}
std::cout << "-----" << std::endl;
for (auto& ix : x)
{
std::cout << ix << std::endl;
}
}
#endif
int main()
{
#if Fn1
fn1();
#endif
#if Fn2
fn2();
#endif
}
错误:
In instantiation of 'struct reverse_wrapper':
61:30: required from here
14:56: error: no matching function for call to 'end(myArray&)'
14:56: note: candidates are:
In file included from /usr/include/c++/4.9/string:51:0,
from /usr/include/c++/4.9/bits/locale_classes.h:40,
from /usr/include/c++/4.9/bits/ios_base.h:41,
from /usr/include/c++/4.9/ios:42,
from /usr/include/c++/4.9/ostream:38,
from /usr/include/c++/4.9/iterator:64,
from 1:
/usr/include/c++/4.9/bits/range_access.h:68:5: note: template decltype (__cont.end()) std::end(_Container&)
end(_Container& __cont) -> decltype(__cont.end())
^
/usr/include/c++/4.9/bits/range_access.h:68:5: note: template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.end()) std::end(_Container&) [with _Container = myArray]':
14:56: required from 'struct reverse_wrapper'
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:68:5: error: 'struct myArray' has no member named 'end'
In instantiation of 'struct reverse_wrapper':
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:78:5: note: template decltype (__cont.end()) std::end(const _Container&)
end(const _Container& __cont) -> decltype(__cont.end())
^
/usr/include/c++/4.9/bits/range_access.h:78:5: note: template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.end()) std::end(const _Container&) [with _Container = myArray]':
14:56: required from 'struct reverse_wrapper'
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:78:5: error: 'const struct myArray' has no member named 'end'
In instantiation of 'struct reverse_wrapper':
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:97:5: note: template _Tp* std::end(_Tp (&)[_Nm])
end(_Tp (&__arr)[_Nm])
^
/usr/include/c++/4.9/bits/range_access.h:97:5: note: template argument deduction/substitution failed:
14:56: note: mismatched types '_Tp [_Nm]' and 'myArray'
In file included from /usr/include/c++/4.9/bits/basic_string.h:42:0,
from /usr/include/c++/4.9/string:52,
from /usr/include/c++/4.9/bits/locale_classes.h:40,
from /usr/include/c++/4.9/bits/ios_base.h:41,
from /usr/include/c++/4.9/ios:42,
from /usr/include/c++/4.9/ostream:38,
from /usr/include/c++/4.9/iterator:64,
from 1:
/usr/include/c++/4.9/initializer_list:99:5: note: template constexpr const _Tp* std::end(std::initializer_list)
end(initializer_list __ils) noexcept
^
/usr/include/c++/4.9/initializer_list:99:5: note: template argument deduction/substitution failed:
14:56: note: 'myArray' is not derived from 'std::initializer_list'
20:58: error: no matching function for call to 'begin(myArray&)'
20:58: note: candidates are:
In file included from /usr/include/c++/4.9/string:51:0,
from /usr/include/c++/4.9/bits/locale_classes.h:40,
from /usr/include/c++/4.9/bits/ios_base.h:41,
from /usr/include/c++/4.9/ios:42,
from /usr/include/c++/4.9/ostream:38,
from /usr/include/c++/4.9/iterator:64,
from 1:
/usr/include/c++/4.9/bits/range_access.h:48:5: note: template decltype (__cont.begin()) std::begin(_Container&)
begin(_Container& __cont) -> decltype(__cont.begin())
^
/usr/include/c++/4.9/bits/range_access.h:48:5: note: template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.begin()) std::begin(_Container&) [with _Container = myArray]':
20:58: required from 'struct reverse_wrapper'
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:48:5: error: 'struct myArray' has no member named 'begin'
In instantiation of 'struct reverse_wrapper':
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:58:5: note: template decltype (__cont.begin()) std::begin(const _Container&)
begin(const _Container& __cont) -> decltype(__cont.begin())
^
/usr/include/c++/4.9/bits/range_access.h:58:5: note: template argument deduction/substitution failed:
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.begin()) std::begin(const _Container&) [with _Container = myArray]':
20:58: required from 'struct reverse_wrapper'
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:58:5: error: 'const struct myArray' has no member named 'begin'
In instantiation of 'struct reverse_wrapper':
61:30: required from here
/usr/include/c++/4.9/bits/range_access.h:87:5: note: template _Tp* std::begin(_Tp (&)[_Nm])
begin(_Tp (&__arr)[_Nm])
^
/usr/include/c++/4.9/bits/range_access.h:87:5: note: template argument deduction/substitution failed:
20:58: note: mismatched types '_Tp [_Nm]' and 'myArray'
In file included from /usr/include/c++/4.9/bits/basic_string.h:42:0,
from /usr/include/c++/4.9/string:52,
from /usr/include/c++/4.9/bits/locale_classes.h:40,
from /usr/include/c++/4.9/bits/ios_base.h:41,
from /usr/include/c++/4.9/ios:42,
from /usr/include/c++/4.9/ostream:38,
from /usr/include/c++/4.9/iterator:64,
from 1:
/usr/include/c++/4.9/initializer_list:89:5: note: template constexpr const _Tp* std::begin(std::initializer_list)
begin(initializer_list __ils) noexcept
^
/usr/include/c++/4.9/initializer_list:89:5: note: template argument deduction/substitution failed:
20:58: note: 'myArray' is not derived from 'std::initializer_list'
In function 'void fn2()':
61:30: error: invalid initialization of reference of type 'myArray&' from expression of type 'reverse_wrapper'
38:6: note: in passing argument 1 of 'int* begin(myArray&)'
61:30: error: invalid initialization of reference of type 'myArray&' from expression of type 'reverse_wrapper'
39:8: note: in passing argument 1 of 'int* end(myArray&)'
为了回应评论,让我写一些解决方法来解决缺少自动推导的 return 类型。
总而言之,问题是您使用的是对 begin
和 end
的命名空间限定调用,而实际上您只是使用最合适的调用,而使用 std::
实施作为备份。
由于我在评论中提出的解决方案不起作用,你可以试试这个。
在包含reverse_wrapper
的header中,可以添加这个方法
namespace detail {
using std::begin;
using std::end;
template< typename Container >
auto impl_begin( Container & c ) -> decltype( begin(c) ) {
return begin(c);
}
// Same for end
}
template< typename Container >
class reverse_wrapper {
Container& container;
public:
auto end() -> decltype( detail::impl_begin(container) ) {
return std::reverse_iterator<decltype( detail::impl_begin(container) )>( detail::impl_begin(container) );
}
// ... and the rest
};
我能想到三种可能的解决方案,其中一种已经在中描述过;考虑到您使用的是 VS 2013,我建议您使用那个,但我想我会提供另外两个供参考。
想到的第一个解决方案是,如果可能的话,将成员函数begin()
和end()
添加到myArray
(以及它充当占位符的所有类型) ;最简单的方法是将函数 int* begin(myArray&)
和 int* end(myArray&)
转换为成员函数。这允许 std::begin()
和 std::end()
到 return 适当的迭代器,这反过来又允许 reverse_wrapper::begin()
和 reverse_wrapper::end()
工作。
struct myArray
{
int elements[5] = {1,2,3,4,5};
int* begin() { return &elements[0]; }
int* end() { return &elements[5]; }
};
// int* begin(myArray& array) { return &array.elements[0]; }
// int* end(myArray& array) { return &array.elements[5]; }
这是因为for any instance c
of container class C
, std::begin()
will take C& c
and return exactly c.begin()
. Likewise, std::end()
will take C& c
and return exactly c.end()
.
请注意,这将需要修改每个用户定义的容器类型以使其具有成员函数 begin()
和 end()
(如果它还没有),这可能很耗时并且可能容易出错的任务。如果从头开始制作容器,我会推荐这种方法,但如果使用将 begin()
和 end()
定义为独立函数的预先存在的容器,我不会推荐这种方法。
或者,正如 KaBoissonneault 在评论中指出的那样,您可以使用 using 声明 using std::begin;
和 using std::end;
,前提是您的编译器可以执行 return 类型推导.
template <typename Container>
struct reverse_wrapper
{
Container& container;
auto begin()
// -> std::reverse_iterator< decltype(std::end(container)) >
{
using std::end;
return std::reverse_iterator< decltype(/*std::*/end(container)) >(/*std::*/end(container));
}
auto end()
// -> std::reverse_iterator< decltype(std::begin(container)) >
{
using std::begin;
return std::reverse_iterator< decltype(/*std::*/begin(container)) >(/*std::*/begin(container));
}
};
这在不支持 return 类型推导的编译器上是不可能的;在最大的 3 个编译器中,它需要 Clang 3.4 或更高版本、GCC 4.9 或更高版本或 Visual Studio 2015。如果使用这些版本之前的版本,则需要使用不同的解决方案。
或者,如 所解释的,您可以将实现细节隐藏在命名空间中,即使编译器不支持 return 类型推导,您也可以利用 using 声明的好处.这是最简单的解决方案,需要的更改最少。由于不需要任何 C++14 功能,它也是最有可能被任何给定编译器支持的一种。
我正在尝试为使用双向迭代器的容器编写一个通用的反向包装器 std::reverse_iterator
。
但是,当编译器查找 begin(...)
或 end(...)
时,它会说找不到对 reverse_wrapper<CONTAINER>::begin(container)
的匹配函数调用,因为候选需要 0 个参数,但提供了 1 个。
我猜这是因为 std::begin(myArray&)
和 std::end(myArray&)
不存在。强制他们进入 std
命名空间是行不通的(无论如何都是不可取的)。我还尝试从我的 reverse_wrapper
中删除 std::
前缀,但这没有用,而且还会破坏正在工作的 std
容器实现。
这似乎是范围解析问题,但我似乎无法解决。我做错了什么?
代码:
#include <iterator>
#include <iostream>
#include <vector>
#define Fn1 0 // std container WORKS
#define Fn2 1 // non-std container DOESN'T WORK
template <typename Container>
struct reverse_wrapper
{
Container& container;
auto begin()
-> std::reverse_iterator< decltype(std::end(container)) >
{
return std::reverse_iterator< decltype(std::end(container)) >(std::end(container));
}
auto end()
-> std::reverse_iterator< decltype(std::begin(container)) >
{
return std::reverse_iterator< decltype(std::begin(container)) >(std::begin(container));
}
};
template <typename Container>
auto reverse(Container&& container)
-> reverse_wrapper<Container>
{
return{ container };
}
struct myArray
{
int elements[5] = {1,2,3,4,5};
};
int* begin(myArray& array) { return &array.elements[0]; }
int* end(myArray& array) { return &array.elements[5]; }
#if Fn1
void fn1()
{
std::vector<int> x = { 1,2,3,4,5 };
for (auto& ix : reverse(x))
{
std::cout << ix << std::endl;
}
std::cout << "-----" << std::endl;
for (auto& ix : x)
{
std::cout << ix << std::endl;
}
}
#endif
#if Fn2
void fn2()
{
myArray x;
for (auto& ix : reverse(x))
{
std::cout << ix << std::endl;
}
std::cout << "-----" << std::endl;
for (auto& ix : x)
{
std::cout << ix << std::endl;
}
}
#endif
int main()
{
#if Fn1
fn1();
#endif
#if Fn2
fn2();
#endif
}
错误:
In instantiation of 'struct reverse_wrapper': 61:30: required from here 14:56: error: no matching function for call to 'end(myArray&)' 14:56: note: candidates are: In file included from /usr/include/c++/4.9/string:51:0, from /usr/include/c++/4.9/bits/locale_classes.h:40, from /usr/include/c++/4.9/bits/ios_base.h:41, from /usr/include/c++/4.9/ios:42, from /usr/include/c++/4.9/ostream:38, from /usr/include/c++/4.9/iterator:64, from 1: /usr/include/c++/4.9/bits/range_access.h:68:5: note: template decltype (__cont.end()) std::end(_Container&) end(_Container& __cont) -> decltype(__cont.end()) ^ /usr/include/c++/4.9/bits/range_access.h:68:5: note: template argument deduction/substitution failed: /usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.end()) std::end(_Container&) [with _Container = myArray]': 14:56: required from 'struct reverse_wrapper' 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:68:5: error: 'struct myArray' has no member named 'end' In instantiation of 'struct reverse_wrapper': 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:78:5: note: template decltype (__cont.end()) std::end(const _Container&) end(const _Container& __cont) -> decltype(__cont.end()) ^ /usr/include/c++/4.9/bits/range_access.h:78:5: note: template argument deduction/substitution failed: /usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.end()) std::end(const _Container&) [with _Container = myArray]': 14:56: required from 'struct reverse_wrapper' 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:78:5: error: 'const struct myArray' has no member named 'end' In instantiation of 'struct reverse_wrapper': 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:97:5: note: template _Tp* std::end(_Tp (&)[_Nm]) end(_Tp (&__arr)[_Nm]) ^ /usr/include/c++/4.9/bits/range_access.h:97:5: note: template argument deduction/substitution failed: 14:56: note: mismatched types '_Tp [_Nm]' and 'myArray' In file included from /usr/include/c++/4.9/bits/basic_string.h:42:0, from /usr/include/c++/4.9/string:52, from /usr/include/c++/4.9/bits/locale_classes.h:40, from /usr/include/c++/4.9/bits/ios_base.h:41, from /usr/include/c++/4.9/ios:42, from /usr/include/c++/4.9/ostream:38, from /usr/include/c++/4.9/iterator:64, from 1: /usr/include/c++/4.9/initializer_list:99:5: note: template constexpr const _Tp* std::end(std::initializer_list) end(initializer_list __ils) noexcept ^ /usr/include/c++/4.9/initializer_list:99:5: note: template argument deduction/substitution failed: 14:56: note: 'myArray' is not derived from 'std::initializer_list' 20:58: error: no matching function for call to 'begin(myArray&)' 20:58: note: candidates are: In file included from /usr/include/c++/4.9/string:51:0, from /usr/include/c++/4.9/bits/locale_classes.h:40, from /usr/include/c++/4.9/bits/ios_base.h:41, from /usr/include/c++/4.9/ios:42, from /usr/include/c++/4.9/ostream:38, from /usr/include/c++/4.9/iterator:64, from 1: /usr/include/c++/4.9/bits/range_access.h:48:5: note: template decltype (__cont.begin()) std::begin(_Container&) begin(_Container& __cont) -> decltype(__cont.begin()) ^ /usr/include/c++/4.9/bits/range_access.h:48:5: note: template argument deduction/substitution failed: /usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.begin()) std::begin(_Container&) [with _Container = myArray]': 20:58: required from 'struct reverse_wrapper' 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:48:5: error: 'struct myArray' has no member named 'begin' In instantiation of 'struct reverse_wrapper': 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:58:5: note: template decltype (__cont.begin()) std::begin(const _Container&) begin(const _Container& __cont) -> decltype(__cont.begin()) ^ /usr/include/c++/4.9/bits/range_access.h:58:5: note: template argument deduction/substitution failed: /usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.begin()) std::begin(const _Container&) [with _Container = myArray]': 20:58: required from 'struct reverse_wrapper' 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:58:5: error: 'const struct myArray' has no member named 'begin' In instantiation of 'struct reverse_wrapper': 61:30: required from here /usr/include/c++/4.9/bits/range_access.h:87:5: note: template _Tp* std::begin(_Tp (&)[_Nm]) begin(_Tp (&__arr)[_Nm]) ^ /usr/include/c++/4.9/bits/range_access.h:87:5: note: template argument deduction/substitution failed: 20:58: note: mismatched types '_Tp [_Nm]' and 'myArray' In file included from /usr/include/c++/4.9/bits/basic_string.h:42:0, from /usr/include/c++/4.9/string:52, from /usr/include/c++/4.9/bits/locale_classes.h:40, from /usr/include/c++/4.9/bits/ios_base.h:41, from /usr/include/c++/4.9/ios:42, from /usr/include/c++/4.9/ostream:38, from /usr/include/c++/4.9/iterator:64, from 1: /usr/include/c++/4.9/initializer_list:89:5: note: template constexpr const _Tp* std::begin(std::initializer_list) begin(initializer_list __ils) noexcept ^ /usr/include/c++/4.9/initializer_list:89:5: note: template argument deduction/substitution failed: 20:58: note: 'myArray' is not derived from 'std::initializer_list' In function 'void fn2()': 61:30: error: invalid initialization of reference of type 'myArray&' from expression of type 'reverse_wrapper' 38:6: note: in passing argument 1 of 'int* begin(myArray&)' 61:30: error: invalid initialization of reference of type 'myArray&' from expression of type 'reverse_wrapper' 39:8: note: in passing argument 1 of 'int* end(myArray&)'
为了回应评论,让我写一些解决方法来解决缺少自动推导的 return 类型。
总而言之,问题是您使用的是对 begin
和 end
的命名空间限定调用,而实际上您只是使用最合适的调用,而使用 std::
实施作为备份。
由于我在评论中提出的解决方案不起作用,你可以试试这个。
在包含reverse_wrapper
的header中,可以添加这个方法
namespace detail {
using std::begin;
using std::end;
template< typename Container >
auto impl_begin( Container & c ) -> decltype( begin(c) ) {
return begin(c);
}
// Same for end
}
template< typename Container >
class reverse_wrapper {
Container& container;
public:
auto end() -> decltype( detail::impl_begin(container) ) {
return std::reverse_iterator<decltype( detail::impl_begin(container) )>( detail::impl_begin(container) );
}
// ... and the rest
};
我能想到三种可能的解决方案,其中一种已经在
想到的第一个解决方案是,如果可能的话,将成员函数begin()
和end()
添加到myArray
(以及它充当占位符的所有类型) ;最简单的方法是将函数 int* begin(myArray&)
和 int* end(myArray&)
转换为成员函数。这允许 std::begin()
和 std::end()
到 return 适当的迭代器,这反过来又允许 reverse_wrapper::begin()
和 reverse_wrapper::end()
工作。
struct myArray
{
int elements[5] = {1,2,3,4,5};
int* begin() { return &elements[0]; }
int* end() { return &elements[5]; }
};
// int* begin(myArray& array) { return &array.elements[0]; }
// int* end(myArray& array) { return &array.elements[5]; }
这是因为for any instance c
of container class C
, std::begin()
will take C& c
and return exactly c.begin()
. Likewise, std::end()
will take C& c
and return exactly c.end()
.
请注意,这将需要修改每个用户定义的容器类型以使其具有成员函数 begin()
和 end()
(如果它还没有),这可能很耗时并且可能容易出错的任务。如果从头开始制作容器,我会推荐这种方法,但如果使用将 begin()
和 end()
定义为独立函数的预先存在的容器,我不会推荐这种方法。
或者,正如 KaBoissonneault 在评论中指出的那样,您可以使用 using 声明 using std::begin;
和 using std::end;
,前提是您的编译器可以执行 return 类型推导.
template <typename Container>
struct reverse_wrapper
{
Container& container;
auto begin()
// -> std::reverse_iterator< decltype(std::end(container)) >
{
using std::end;
return std::reverse_iterator< decltype(/*std::*/end(container)) >(/*std::*/end(container));
}
auto end()
// -> std::reverse_iterator< decltype(std::begin(container)) >
{
using std::begin;
return std::reverse_iterator< decltype(/*std::*/begin(container)) >(/*std::*/begin(container));
}
};
这在不支持 return 类型推导的编译器上是不可能的;在最大的 3 个编译器中,它需要 Clang 3.4 或更高版本、GCC 4.9 或更高版本或 Visual Studio 2015。如果使用这些版本之前的版本,则需要使用不同的解决方案。
或者,如