2016-09-30 102 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

    相關問題