2014-11-16 3 views
7

최근에 저는 매우 어려운 const-correctness 컴파일러 오류를 수정하려고했습니다. 처음에는 Boost.Python 내에 여러 단락 템플릿 구토 오류가있었습니다.std :: begin 및 R-values ​​

그러나 이것은 중요하지 않습니다. C++ 11 std::beginstd::end 반복자 함수는 R 값을 얻기 위해 오버로드되지 않습니다. std::begin

정의 (들)은 다음과 같습니다에는 R 값/유니버설 참조 과부하가 없기 때문에 당신이 그것에게 R-값을 전달하면

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

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

그래서, 당신은 const를 반복자를 얻을.

그럼 왜 신경 써야하나요? 예를 들어 "view", "proxy"또는 "slice"또는 다른 컨테이너의 하위 반복자 범위를 나타내는 일부 컨테이너 유형과 같은 "범위"컨테이너 유형이있는 경우에는 R 값 의미를 사용하고 임시 슬라이스/범위 객체에서 비 const 반복기를 가져옵니다. 그러나 std::begin을 사용하면 std::begin이 항상 R 값의 const 반복기를 반환하기 때문에 운이 나빠집니다. 이것은 C++ 11 프로그래머가 종종 C++ 11이 R 값을주기 전날에 좌절했던 오래된 문제입니다. 즉 임시 직원 문제는 항상 const이라는 바인딩입니다. c 우리는 그렇지 C::const_iteratorC::iterator을 얻을 일정한 경우,

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

이 방법 :

그럼, 왜로 정의되지 std::begin된다.

처음에는 그 이유가 안전 때문이라고 생각했습니다. 당신이 std::begin에 임시 전달과 같이하는 경우 :

auto it = std::begin(std::string("temporary string")); // never do this 

... 당신은 잘못된 반복자를 얻을 것입니다. 그러나 그때 나는이 구현이 여전히 존재한다는 것을 깨달았다. 위의 코드는 잘못된 const -iterator를 반환하며, 역 참조 할 때 segfault가됩니다.

그래서 std::begin이 아닌가요? R 값 (정확하게는 Universal Reference)이되도록 정의 된 이유는 무엇입니까? 과부하가 두 개있는 이유는 무엇입니까 (const에 하나, non-const에 하나).

+1

'std :: forward (c)'을 잊어 버렸습니다. – Columbo

+1

이 경우 문제가 될지 잘 모르겠다 -이 경우 중요한 것은 'c'는'const'이거나'C &&'가'C &'로 변한 후에 영향을받지 않는 문제이다. – Siler

+0

컨테이너가 ref-qualifier로'begin '을 오버로드하여 반환 된 반복자 유형을 객체 인수의 값 범주에 종속되게 만듭니다. 그러나 맞지 않는 시범 목적을 위해. – Columbo

답변

6

위의 코드는 단순히 잘못된 const를 반복자에게

그렇진를 반환합니다. 반복자는 임시 반복자는 어휘에서 만든을 의미하는 전체 표현이 끝날 때까지 유효합니다.

std::copy_n(std::begin(std::string("Hallo")), 2, 
      std::ostreambuf_iterator<char>(std::cout)); 

은 여전히 ​​유효 코드 무언가

같은. 물론, 예제에서 it은 명령문의 끝 부분에서 무효화됩니다.

임시 또는 x 값을 수정할 때 어떤 점이 있습니까? 아마도 선언을 제안 할 때 범위 접근 자의 디자이너가 염두에 두었던 질문 중 하나 일 것입니다.그들은 .begin().end()에 의해 반환 된 반복자가 수명보다 오래 유효한 "프록시"범위를 고려하지 않았습니다. 아마도 템플릿 코드에서 정상 범위와 구별 할 수 없기 때문에 아마도 임시 비 프록시 범위를 수정하고 싶지는 않습니다. 그 이유는 무의미하고 혼란을 야기 할 수 있기 때문입니다.

그러나 처음에 std::begin를 사용할 필요가 없습니다 오히려 사용-선언을 선언 할 수 :

using std::begin; 
using std::end; 

및 ADL을 사용합니다. Boost.Python (o.s.)이 사용하고 제한을 회피하는 유형에 대해 std::begin의 네임 스페이스 범위 beginend 오버로드를 선언합니다. 예 :

iterator begin(boost_slice&& s) { return s.begin(); } 
iterator end (boost_slice&& s) { return s.end() ; } 

// […] 

begin(some_slice) // Calls the global overload, returns non-const iterator 

왜이 과부하 (const를위한 하나 const가 아닌 하나)가?

rvalues ​​개체가 계속 지원되기를 원하므로 (T& 형식의 함수 매개 변수로는 사용할 수 없기 때문에).

관련 문제