【问题标题】:Why doesn't the scope resolution not go outside of the class?为什么范围解析不超出课程范围?
【发布时间】:2016-11-22 16:15:59
【问题描述】:

我正在尝试为使用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
}

错误:

在“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

【问题讨论】:

  • 去掉std::beginstd::end中的std::,然后在你使用beginend的地方加上using std::begin; using std::end。在某些情况下,使用自动推导的返回类型而不是尾随返回类型可能会更好
  • @KABoissonneault,是的,我曾想过使用using std::begin/end,但它在类范围内不起作用,因此返回类型找不到它。我也想过使用自动推导的类型,但这在 VS2013 中不起作用。
  • 在命名空间范围内使用它们怎么样?
  • @KABoissonneault,因为这些类在标题中,所以不是最佳的。 myArray 只是分散在其他数组实现的占位符,有些在基本范围内,有些在 std
  • 好吧,但是在这方面,我还是觉得myArray应该提供自己的迭代器,然后reverse_wrapper可以简单的委托给容器而不用直接使用reverse_iteratortemplate &lt;typename Container&gt; struct reverse_wrapper { Container&amp; container; auto begin() { return std::rbegin(container); } auto end() { return std::rend(container); } };跨度>

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


【解决方案1】:

针对cmets,让我为缺少自动推导的返回类型写一些解决方法。

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

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

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

【讨论】:

    【解决方案2】:

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

    想到的第一个解决方案是,如果可能的话,将成员函数begin()end() 添加到myArray(以及它充当占位符的所有类型);最简单的方法是将函数int* begin(myArray&amp;)int* end(myArray&amp;) 转换为成员函数。这允许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&amp; c and return exactly c.begin()。同样,std::end() will take C&amp; c and return exactly c.end()

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


    或者,正如 KaBoissonneault 在 cmets 中指出的那样,您可以使用 using-declarations 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 功能,它也是任何给定编译器最有可能支持的一种。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-10
      • 2018-03-06
      • 2015-11-05
      • 2020-03-14
      • 2023-03-06
      • 1970-01-01
      • 2011-12-03
      相关资源
      最近更新 更多