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

aut*_*tec 6 c++ c++11

这是相关代码的链接:

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

我遇到了这种行为,因为declval<Container>()decltype表达式中std::begin.gcc和clang都返回迭代器,在解除引用时会产生const引用.它可能是有意义的,因为r值引用通常绑定到您不想变异的过期对象.但是,我找不到任何关于此的文件来确定它是否符合标准.我找不到任何相关的重载begin()或重新限定的重载Container::begin().

更新:答案澄清了正在发生的事情,但相互作用可能很微妙,如下所示:

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

天真地,你不会期望(a)和(b)的不同结果当:: begin刚刚用调用vector :: begin来定义时.但是缺少std :: begin重载,它采用非const r值引用并返回迭代器(或者返回const_iterator的ref-qualified vector :: begin overload)会导致这种情况发生.

Jar*_*d42 8

正如您在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());
Run Code Online (Sandbox Code Playgroud)

并且std::vector<int>&&只能绑定到第二个重载(所以返回const_iterator).


Vit*_*meo 6

让我们一步一步地分析一下发生了什么:

  1. 你正在调用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());
    
    Run Code Online (Sandbox Code Playgroud)

  1. 由于引用折叠,临时(xvalue)将仅绑定到const左值引用:

    如果使用xvalue调用Fwd,我们再次将Type &&作为v的类型.这将不允许您调用带有非const左值的函数,因为xvalue无法绑定到非const左值引用.它可以绑定到const左值引用,所以如果Call使用const&,我们可以用xvalue调用Fwd.

    (来自链接的答案).


  1. 因此,

     template<class C> auto begin(const C& c) -> decltype(c.begin());
    
    Run Code Online (Sandbox Code Playgroud)

    正在调用重载,它返回一个const迭代器.

    为什么?

    因为std::begin(v)调用v.begin(),实例上调用返回.const_iteratorconststd::vector