为什么范围分辨率不在课堂之外?

时间:2016-07-19 16:38:57

标签: c++11 visual-studio-2013 iterator reverse-iterator

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

然而,当编译器查找begin(...)end(...)时,它似乎会说找不到与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
}

错误:

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&)'
 

Demo

2 个答案:

答案 0 :(得分:3)

在回复评论时,让我为缺少自动推断的返回类型编写一些解决方法。

总而言之,问题在于,在使用begin时,当您实际上只使用最适合的endstd::时,您正在使用符合命名空间的调用实施作为备份。

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

在包含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
};

答案 1 :(得分:1)

我可以想到三种可能的解决方案,其中一种已在another answer中描述过;我建议使用那个,考虑到你使用的是VS 2013,但我认为我会提供其他两个作为参考。

如果可能,首先想到的解决方案是将成员函数begin()end()添加到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]; }

这是因为for any instance c of container class C, std::begin() will take C& c and return exactly c.begin()。同样,std::end() will take C& c and return exactly c.end()

请注意,这将需要修改每个用户定义的容器类型,使其具有成员函数begin()end()(如果它还没有它们),这可能非常耗时且可能出错 - 俯卧任务。如果从头开始创建容器,我建议使用此方法,但如果使用将begin()end()定义为独立函数的预先存在的容器,则不建议这样做。

或者,正如注释中指出的KaBoissonneault,您可以使用using声明using 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's answer所解释的那样,您可以隐藏命名空间中的实现细节,即使编译器不支持返回类型推导,也可以获得使用声明的好处。这是最简单的解决方案,需要的更改量最少。由于不需要任何C ++ 14功能,它也是任何给定编译器最有可能支持的。