1

我正在尝试为使用双向迭代器的容器编写一个通用的反向包装器,使用std::reverse_iterator.

但是,似乎当编译器查找begin(...)orend(...)时,它会说找不到匹配的函数调用,reverse_wrapper<CONTAINER>::begin(container)因为候选者需要 0 个参数,但提供了 1 个参数。

我会猜到这是因为std::begin(myArray&)并且std::end(myArray&)不存在。强制他们进入std命名空间是行不通的(无论如何也不可取)。我也尝试std::从我的中删除前缀,reverse_wrapper但这没有用,并且还会破坏工作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
}

错误:

在“struct reverse_wrapper”的实例化中:
61:30:从这里需要
14:56:错误:没有匹配函数调用“end(myArray&)”
14:56:注:候选人为:
在 /usr/include/c++/4.9/string:51:0 包含的文件中,
                 来自 /usr/include/c++/4.9/bits/locale_classes.h:40,
                 来自 /usr/include/c++/4.9/bits/ios_base.h:41,
                 来自/usr/include/c++/4.9/ios:42,
                 来自/usr/include/c++/4.9/ostream:38,
                 来自 /usr/include/c++/4.9/iterator:64,
                 从1:
/usr/include/c++/4.9/bits/range_access.h:68:5: 注意:模板 decltype (__cont.end()) std::end(_Container&)
     end(_Container& __cont) -> decltype(__cont.end())
     ^
/usr/include/c++/4.9/bits/range_access.h:68:5: 注意:模板参数推导/替换失败:
/usr/include/c++/4.9/bits/range_access.h:代替'template decltype (__cont.end()) std::end(_Container&) [with _Container = myArray]':
14:56:'struct reverse_wrapper' 需要
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:68:5: 错误:“struct myArray”没有名为“end”的成员
 在“struct reverse_wrapper”的实例化中:
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:78:5: 注意:模板 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:注意:模板参数推导/替换失败:
/usr/include/c++/4.9/bits/range_access.h:代替'template decltype (__cont.end()) std::end(const _Container&) [with _Container = myArray]':
14:56:'struct reverse_wrapper' 需要
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:78:5:错误:“const struct myArray”没有名为“end”的成员
 在“struct reverse_wrapper”的实例化中:
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:97:5: 注意:模板 _Tp* std::end(_Tp (&)[_Nm])
     结束(_Tp (&__arr)[_Nm])
     ^
/usr/include/c++/4.9/bits/range_access.h:97:5:注意:模板参数推导/替换失败:
14:56:注意:不匹配的类型 '_Tp [_Nm]' 和 'myArray'
在 /usr/include/c++/4.9/bits/basic_string.h:42:0 包含的文件中,
                 来自 /usr/include/c++/4.9/string:52,
                 来自 /usr/include/c++/4.9/bits/locale_classes.h:40,
                 来自 /usr/include/c++/4.9/bits/ios_base.h:41,
                 来自/usr/include/c++/4.9/ios:42,
                 来自/usr/include/c++/4.9/ostream:38,
                 来自 /usr/include/c++/4.9/iterator:64,
                 从1:
/usr/include/c++/4.9/initializer_list:99:5: 注意:模板 constexpr const _Tp* std::end(std::initializer_list)
     end(initializer_list __ils) noexcept
     ^
/usr/include/c++/4.9/initializer_list:99:5:注意:模板参数推导/替换失败:
14:56:注意:“myArray”不是从“std::initializer_list”派生的
20:58:错误:没有匹配函数调用“begin(myArray&)”
20:58:注:候选人是:
在 /usr/include/c++/4.9/string:51:0 包含的文件中,
                 来自 /usr/include/c++/4.9/bits/locale_classes.h:40,
                 来自 /usr/include/c++/4.9/bits/ios_base.h:41,
                 来自/usr/include/c++/4.9/ios:42,
                 来自/usr/include/c++/4.9/ostream:38,
                 来自 /usr/include/c++/4.9/iterator:64,
                 从1:
/usr/include/c++/4.9/bits/range_access.h:48:5: 注意:模板 decltype (__cont.begin()) std::begin(_Container&)
     开始(_Container& __cont) -> decltype(__cont.begin())
     ^
/usr/include/c++/4.9/bits/range_access.h:48:5:注意:模板参数推导/替换失败:
/usr/include/c++/4.9/bits/range_access.h:代替'template decltype (__cont.begin()) std::begin(_Container&) [with _Container = myArray]':
20:58:从“struct reverse_wrapper”中需要
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:48:5: 错误:“struct myArray”没有名为“begin”的成员
 在“struct reverse_wrapper”的实例化中:
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:58:5: 注意:模板 decltype (__cont.begin()) std::begin(const _Container&)
     开始(const _Container& __cont) -> decltype(__cont.begin())
     ^
/usr/include/c++/4.9/bits/range_access.h:58:5:注意:模板参数推导/替换失败:
/usr/include/c++/4.9/bits/range_access.h:代替'template decltype (__cont.begin()) std::begin(const _Container&) [with _Container = myArray]':
20:58:从“struct reverse_wrapper”中需要
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:58:5:错误:“const struct myArray”没有名为“begin”的成员
 在“struct reverse_wrapper”的实例化中:
61:30:从这里需要
/usr/include/c++/4.9/bits/range_access.h:87:5: 注意:模板 _Tp* std::begin(_Tp (&)[_Nm])
     开始(_Tp (&__arr)[_Nm])
     ^
/usr/include/c++/4.9/bits/range_access.h:87:5:注意:模板参数推导/替换失败:
20:58:注意:不匹配的类型 '_Tp [_Nm]' 和 'myArray'
在 /usr/include/c++/4.9/bits/basic_string.h:42:0 包含的文件中,
                 来自 /usr/include/c++/4.9/string:52,
                 来自 /usr/include/c++/4.9/bits/locale_classes.h:40,
                 来自 /usr/include/c++/4.9/bits/ios_base.h:41,
                 来自/usr/include/c++/4.9/ios:42,
                 来自/usr/include/c++/4.9/ostream:38,
                 来自 /usr/include/c++/4.9/iterator:64,
                 从1:
/usr/include/c++/4.9/initializer_list:89:5: 注意:模板 constexpr const _Tp* std::begin(std::initializer_list)
     begin(initializer_list __ils) noexcept
     ^
/usr/include/c++/4.9/initializer_list:89:5:注意:模板参数推导/替换失败:
20:58:注意:“myArray”不是从“std::initializer_list”派生的
 在函数 'void fn2()' 中:
61:30:错误:从“reverse_wrapper”类型的表达式中对“myArray&”类型的引用进行无效初始化
38:6:注意:传递 'int* begin(myArray&)' 的参数 1
61:30:错误:从“reverse_wrapper”类型的表达式中对“myArray&”类型的引用进行无效初始化
39:8:注意:传递 'int* end(myArray&)' 的参数 1
 

Demo

4

2 回答 2

3

作为对评论的回应,让我为缺少自动推断的返回类型写一些解决方法。

总而言之,问题在于您正在使用命名空间限定的调用,beginend实际上您只是使用最适合的调用,同时使用std::实现作为备份。

由于我在评论中提出的解决方案不起作用,你可以试试这个。

在包含 的标题中reverse_wrapper,您可以添加此方法

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
};
于 2016-07-19T17:20:02.063 回答
1

我能想到三种可能的解决方案,其中一个已经在另一个答案中描述过;考虑到您使用的是 VS 2013,我建议使用那个,但我想我会提供另外两个以供参考。

想到的第一个解决方案是,如果可能的话,添加成员函数begin()end()to myArray(以及它充当占位符的所有类型);最简单的方法是将函数转换int* begin(myArray&)int* end(myArray&)成员函数。这允许std::begin()std::end()返回适当的迭代器,这反过来又允许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]; }

这是因为对于容器类的任何实例,都会准确地获取和返回cCstd::begin()C& cc.begin()。同样,std::end()C& c准确地取回c.end()

请注意,这需要修改每个用户定义的容器类型以具有成员函数begin()end()如果它还没有成员函数,这可能是一项耗时且可能容易出错的任务。如果从头开始制作容器,我会推荐这种方法,但如果使用定义begin()end()作为独立函数的预先存在的容器,则不推荐。


或者,正如KaBoissonneault在评论中指出的那样,您可以使用 using-declarationsusing std::begin;using std::end;,前提是您的编译器可以执行返回类型推导。

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));
    }
};

这在不支持返回类型推导的编译器上是不可能的;在最大的 3 个编译器中,它需要 Clang 3.4 或更高版本、GCC 4.9 或更高版本或 Visual Studio 2015。如果使用这些之前的版本,则需要使用不同的解决方案。


或者,正如KaBoissonneault 的回答所解释的,您可以在命名空间中隐藏实现细节,即使编译器不支持返回类型推导,您也可以享受使用声明的好处。这是最简单的解决方案,并且需要最少的更改。由于不需要任何 C++14 特性,它也是任何给定编译器最有可能支持的一种。

于 2016-07-19T18:24:25.510 回答