cbegin / cend의 이유는 무엇입니까?


답변:


227

아주 간단합니다. 벡터가 있다고 가정 해보십시오.

std::vector<int> vec;

데이터로 채 웁니다. 그런 다음 반복자를 가져오고 싶습니다. 어쩌면 그들을 통과시킬 수 있습니다. 아마 std::for_each:

std::for_each(vec.begin(), vec.end(), SomeFunctor());

C ++ 03에서는 매개 변수 SomeFunctor를 자유롭게 수정할 수있었습니다 . 물론 SomeFunctorvalue 또는 by별로 매개 변수를 사용할 수 const&있지만이를 보장 할 방법이 없습니다 . 이런 바보 같은 일을하지 않으면 :

const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());

이제 소개합니다 cbegin/cend:

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());

이제 SomeFunctor벡터의 요소를 수정할 수없는 구문 보장이 있습니다 (물론 const-cast없이). 우리는 명시 적으로을 얻으 const_iterator므로 SomeFunctor::operator()로 호출됩니다 const int &. 매개 변수를로 사용 int &하면 C ++에서 컴파일러 오류가 발생합니다.


C ++ 17에는이 문제에 대한보다 우아한 해결책이 있습니다 std::as_const. 음, 범위 기반을 사용할 때 적어도 우아합니다 for.

for(auto &item : std::as_const(vec))

const&제공된 객체에 단순히 a 를 반환합니다 .


1
새로운 프로토콜은 vec.cbegin ()이 아니라 cbegin (vec)이라고 생각했습니다.
Kaz Dragon

20
@ Kaz : 존재 std::cbegin/cend하는 방식으로 자유 기능 이 없습니다 std::begin/std::end. 위원회의 감독이었습니다. 이러한 기능이 존재하면 일반적으로 사용하는 방법입니다.
Nicol Bolas

20
분명히 std::cbegin/cendC ++ 14에 추가 될 것입니다. en.cppreference.com/w/cpp/iterator/begin
Adi Shavit

8
@NicolBolas는 ? for(auto &item : std::as_const(vec))와 같습니다 for(const auto &item : vec).
luizfls

8
@luizfls 예. 귀하의 코드는 참조를 설정하여 항목을 수정하지 않을 것이라고 말합니다 const. Nicol은 컨테이너를 const로 간주하므로 참조를 auto추론합니다 const. IMO auto const& item가 더 쉽고 명확합니다. std::as_const()여기서 왜 좋은지 불분명 합니다. const사용되는 유형을 제어 할 수없는 일반적인 코드가 아닌 일반 코드를 전달할 때 유용 할 수 있지만 range-를 사용하면 for노이즈가 추가 된 것처럼 보입니다.
underscore_d

66

Nicol Bolas가 그의 답변 에서 말한 것 외에도 새로운 auto키워드를 고려하십시오 .

auto iterator = container.begin();

를 사용하면 상수가 아닌 컨테이너 참조에 대한 상수 연산자를 반환 auto하도록 할 수있는 방법이 없습니다 begin(). 이제 다음을 수행하십시오.

auto const_iterator = container.cbegin();

2
@allyourcode : 도움이되지 않습니다. 컴파일러 const_iterator에게는 또 다른 식별자 일뿐입니다. 어느 버전은 통상 부재의 typedef의 룩업 사용 decltype(container)::iterator또는 decltype(container)::const_iterator.
aschepler

2
@aschepler 두 번째 문장을 이해하지 못하지만 제 질문에서 "auto"앞에 "const"가 누락 된 것 같습니다. auto가 무엇이든간에 const_iterator는 const이어야합니다.
allyourcode

26
@allyourcode : 그것은 당신에게 일정한 반복자를 줄 것이지만, 그것은 반복자와 상수 데이터와는 매우 다릅니다.
aschepler

2
const_iteratorwith 를 얻는 간단한 방법이 있습니다 . 객체 인수를 규정하기 위해 auto호출되는 보조 함수 템플릿을 작성하십시오 make_const.
Columbo

17
어쩌면 나는 더 이상 C ++ 마인드에 있지 않지만 "간단한 방법"과 "보조 기능 템플릿 작성"의 개념 사이의 연결을 볼 수 없습니다. ;)
Stefan Majewsky

15

이것을 실용적인 유스 케이스로 사용하십시오

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

itconst가 아닌 반복자 이므로 할당이 실패 합니다. 처음에 cbegin을 사용했다면 반복자는 올바른 유형을 가졌을 것입니다.


8

에서 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :

프로그래머는 비 const 컨테이너에서도 const_iterator를 직접 얻을 수 있습니다.

그들은이 예를 주었다

vector<MyType> v;

// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
    // use *it ...
}

그러나 컨테이너 순회가 검사만을 목적으로하는 경우에는 컴파일러가 const-correctness 위반을 진단 할 수 있도록 const_iterator를 사용하는 것이 일반적으로 선호됩니다.

참고 작업 용지는 지금으로 완료되었는지, 어댑터 템플릿을 언급하는 것이 std::begin()std::end()기본 배열과 그 또한 작동합니다. 해당 std::cbegin()std::cend()호기심이 시간으로 누락, 그러나 또한 추가 될 수 있습니다.


5

방금이 질문에 우연히 발견되었습니다 ... 나는 그것이 이미 대답했고 그것이 단지 노드라는 것을 알고 있습니다 ...

auto const it = container.begin() 다른 유형입니다 auto it = container.cbegin()

의 차이 int[5](필자는이 방법을 시작하지만, 잘 차이를 보여 ...하지만 14 C ++로 작업 할 필요가 없습니다 알고 사용하여 포인터, std::cbegin()그리고 std::cend()그것을 여기 때 사람이 무엇을 사용해야 본질적 인) ...

int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers);      // type is int const* -> value is const

2

iteratorconst_iterator상속 관계를 가지고 비교 또는 다른 타입의 할당시 암시 적 변환이 발생한다.

class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
    // ...
}

사용 cbegin()하고 cend()이 경우 성능을 향상시킬 것입니다.

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
    // ...
}

반복자를 초기화하고 비교할 때 변환을 피함으로써 성능이 절약된다는 것을 깨닫는 데 시간이 걸렸습니다. const주된 이점은 성능입니다 ( 비록 의미적이고 정확하며 안전한 코드입니다). 그러나, 당신이 요점을 가지고있는 동안, (A) auto그것을 비 문제로 만듭니다. (B) 성능에 관해 이야기 할 때, 여기서해야 할 중요한 일을 놓쳤습니다 end. for루프 의 초기화 조건에서 사본을 선언 하여 이터레이터를 캐시하고 새 사본을 얻는 대신 그 사본을 비교합니다. 모든 반복에 대한 가치. 그것은 당신의 요점을 더 좋게 만들 것입니다. : P
underscore_d

@underscore_d constconst키워드 자체의 마법 때문에가 아니라 데이터가 수정되지 않는다는 것을 알면 컴파일러가 일부 최적화를 활성화 할 수 있기 때문에 더 나은 성능을 얻는 데 도움 이 될 수 있습니다. 그렇지 않으면 불가능합니다. 이에 대한 실제 예는 Jason Turner의 대화 에서이 비트 를 확인하십시오 .
brainplot

@ brainplot 나는 그것을 할 수 없다고 말하지 않았다. 나는 그것이 주요 이점이 아니며 실제 이익이 의미 적으로 정확하고 안전한 코드 일 때 과장되었다고 생각합니다.
underscore_d

@underscore_d 예, 동의합니다. 나는 단지 const(거의 간접적으로) 성능상의 이점을 이끌어 낼 수 있다는 것을 분명히했다 . 누군가가 이것을 읽는 const다면 "생성 된 코드가 어떤 식 으로든 영향을받지 않는다면 추가를 귀찮게하지 않을 것"이라고 생각할 수 있습니다 . 이것은 사실이 아닙니다.
brainplot

0

간단한 cbegin은 상수 반복자를 반환합니다. 여기서 begin은 반복자를 반환합니다.

더 나은 이해를 위해 여기 두 가지 시나리오를 취할 수 있습니다.

시나리오-1 :

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.begin();i< v.end();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

여기서 반복자 i는 일정하지 않으며 5 씩 증가 할 수 있기 때문에 실행됩니다.

이제 cbegin을 사용하여 상수 반복자 시나리오 -2로 표시합니다.

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.cbegin();i< v.cend();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

상수 반복자를 반환하는 cbegin 및 cend를 사용하여 값을 업데이트 할 수 없으므로 작동하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.