2016-09-30 138 views
6

下面是相关的代码的链接:在C + 11标准中指定std :: begin(Container &&)是否返回const_iterator?

#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

我跑进因为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; 
} 

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

天真,你不会期待不同的结果(一)和(b) when :: begin只是通过调用vector :: begin来定义的。然而,没有使用非const r值引用和返回迭代器(或ref-qualified vector :: begin重载const_iterator重载)的std :: begin重载会导致这种情况发生。

回答

5

让我们试着来分析发生了什么,一步一步:

  1. 你打电话std::begin(std::vector<int>&&),但std::beginhas no overload that takes an rvalue

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

  • 由于reference collapsing,临时(x值)只能绑定到一个const左值参考:

    如果您呼叫的FWD与x值,我们再次拿到类型& &为v的类型,这会不会让你调用一个函数因为xvalue不能绑定到非常量左值引用,所以需要一个非常量左值。它可以绑定到const左值引用,所以如果Call使用const &,我们可以用xvalue调用Fwd。

    (来自链接的答案)


  • 因此,

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

    过载被调用,它返回一个const迭代器。

    为什么?

    因为std::begin(v)电话v.begin()which returns a const_iterator when called on const instances of std::vector.

  • 7

    正如你可以看到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()); 
    

    std::vector<int>&&只能绑定到第二个过载(所以返回const_iterator)。

    +0

    这是有道理的,但它是一个有点令人惊讶的是'decltype(*开始(declval <性病::矢量>()))'是一个常量性病: :string&当矢量和字符串都不是常量时。如果使用vector :: begin,则结果将是字符串&! With :: begin你需要传递vector&declval以避免获得const string&。 [此链接](http://coliru.stacked-crooked.com/a/15c17b288f8d69bd)说明了微妙之处。 – authentec

    +0

    这应该是std :: begin而不是:: begin以上。 – authentec

    相关问题