简体   繁体   English

是否在C ++ 11标准中指定std :: begin(Container &&)返回const_iterator?

[英]Is it specified in the C++11 standard that std::begin(Container&&) returns const_iterator?

Here's a link to relevant code: 这是相关代码的链接:

#include <iostream>
#include <string>
#include <vector>
#include <type_traits>

int main()
{
  std::vector<int> v{1, 2, 3, 4, 5};
  auto iter = begin(std::move(v));
  if(std::is_const<typename std::remove_reference<decltype(*iter)>::type>::value)
    std::cout<<"is const\n";
  return 0;
}

http://coliru.stacked-crooked.com/a/253c6373befe8e50 http://coliru.stacked-crooked.com/a/253c6373befe8e50

I ran into this behavior because of a declval<Container>() in a decltype expression with std::begin . 我遇到了这种行为,因为在带有std::begindecltype表达式中有一个declval<Container>() Both gcc and clang return iterators which yield const references when dereferenced. gcc和clang都返回迭代器,在解除引用时会产生const引用。 It probably makes sense since r-value references usually bind to expiring objects that you don't want to mutate. 它可能是有意义的,因为r值引用通常绑定到您不想变异的过期对象。 However, I could not find any documentation on this to determine whether it's mandated by the standard. 但是,我找不到任何关于此的文件来确定它是否符合标准。 I couldn't find any relevant overloads of begin() or ref-qualified overloads of Container::begin() . 我找不到Container::begin()begin()或ref-qualified重载的任何相关重载。

Update: The answers clarified what's happening but the interactions can be subtle as demonstrated below: 更新:答案澄清了正在发生的事情,但相互作用可能很微妙,如下所示:

#include <iostream>
#include <string>
#include <vector>
#include <type_traits>

int main()
{
  if(std::is_const<typename std::remove_reference<decltype(*begin(std::declval<std::vector<std::string>>()))>::type>::value)
    std::cout<<"(a) is const\n";
  if(!std::is_const<typename std::remove_reference<decltype(*std::declval<std::vector<std::string>>().begin())>::type>::value)
    std::cout<<"(b) is not const\n";
  if(!std::is_const<typename std::remove_reference<decltype(*begin(std::declval<std::vector<std::string>&>()))>::type>::value)
    std::cout<<"(c) is not const\n";
  return 0;
}

http://coliru.stacked-crooked.com/a/15c17b288f8d69bd http://coliru.stacked-crooked.com/a/15c17b288f8d69bd

Naively, you wouldn't expect different results for (a) and (b) when ::begin is just defined in terms of calling vector::begin. 天真地,你不会期望(a)和(b)的不同结果当:: begin刚刚用调用vector :: begin来定义。 However the absence of std::begin overloads that take non-const r-value reference and return iterator (or ref-qualified vector::begin overloads which return const_iterator) cause exactly that to happen. 但是缺少std :: begin重载,它采用非const r值引用并返回迭代器(或者返回const_iterator的ref-qualified vector :: begin overload)会导致这种情况发生。

As you can see in http://en.cppreference.com/w/cpp/iterator/begin the interesting overloads are: 正如您在http://en.cppreference.com/w/cpp/iterator/begin中看到的那样,有趣的重载是:

template<class C> auto begin(C& c) -> decltype(c.begin());
template<class C> auto begin(const C& c) -> decltype(c.begin());

and std::vector<int>&& can only bind to the second overload (so returns const_iterator ). std::vector<int>&&只能绑定到第二个重载(所以返回const_iterator )。

Let's try to analyze what happens, step by step: 让我们一步一步地分析一下发生了什么:

  1. You're calling std::begin(std::vector<int>&&) , but std::begin has no overload that takes an rvalue : 你正在调用std::begin(std::vector<int>&&) ,但是std::begin 没有带rvalue的重载

     template< class C > auto begin( C& c ) -> decltype(c.begin()); template< class C > auto begin( const C& c ) -> decltype(c.begin()); 

  1. Due to reference collapsing , a temporary (xvalue) will only bind to a const lvalue reference: 由于引用折叠 ,临时(xvalue)将仅绑定到const lvalue引用:

    If you call Fwd with an xvalue, we again get Type&& as the type of v. This will not allow you to call a function that takes a non-const lvalue, as an xvalue cannot bind to a non-const lvalue reference. 如果使用xvalue调用Fwd,我们再次将Type &&作为v的类型。这将不允许您调用带有非const左值的函数,因为xvalue无法绑定到非const左值引用。 It can bind to a const lvalue reference, so if Call used a const&, we could call Fwd with an xvalue. 它可以绑定到const左值引用,所以如果Call使用const&,我们可以用xvalue调用Fwd。

    (From the linked answer) . (来自链接的答案)


  1. Therefore, the 因此,

      template<class C> auto begin(const C& c) -> decltype(c.begin()); 

    overload is being called, which returns a const iterator. 正在调用overload,它返回一个const迭代器。

    Why? 为什么?

    Because std::begin(v) calls v.begin() , which returns a const_iterator when called on const instances of std::vector . 因为std::begin(v)调用v.begin()它在std::vector const实例上调用时返回一个const_iterator

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM