C 스타일 배열에서 std :: vector를 초기화하는 방법은 무엇입니까?


174

std::vectorC 스타일 배열에서 초기화하는 가장 저렴한 방법은 무엇입니까 ?

예 : 다음 클래스에는가 vector있지만 외부 제한으로 인해 데이터가 C 스타일 배열로 전달됩니다.

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

분명히, 나는 w_.resize()요소를 호출 하고 반복하거나을 호출 할 수 있습니다 std::copy(). 더 나은 방법이 있습니까?


11
문제의 핵심은 동일한 할당자가 C 스타일 배열을 만드는 데 사용되었는지 벡터가 알 수있는 방법이 없다는 것입니다. 따라서 벡터는 자체 할당자를 사용하여 메모리를 할당해야합니다. 그렇지 않으면 단순히 기본 배열을 교체하고 배열로 바꿀 수 있습니다.
Void

답변:


233

포인터를 반복자로 취급 할 수 있다는 것을 잊지 마십시오.

w_.assign(w, w + len);

3
구현 품질 문제입니다. 반복자에는 카테고리를 지정하는 태그가 있기 때문에 구현시 assign이를 최적화하여 자유롭게 사용할 수 있습니다. 적어도 VC ++에서는 실제로 그렇게합니다.
Pavel Minaev

38
빠른 해결책은 std :: vector <double> w_ (w, w + len);
jamk

1
'w_'에 대해 새로 작성된 스토리지에 요소를 복사합니다. 'w_.data'는 'w'를 가리 키지 않습니다. 여전히 'w'를 할당 해제해야합니다. 소유권 이전이 없습니다
Indy9000

1
끝을지나 하나의 요소 이면 괜찮습니다 ( v.end()반복자가 비슷한 경우에 벡터로 끝을 지나서 가리키는 것). 어설 션을 받으면 다른 곳에서 벗어난 것입니다.
Pavel Minaev

1
벡터가 범위를 벗어날 때 배열 메모리를 할당 해제합니까?
Adrian Koch

41

initialize라는 단어를 사용하여 이것이 일회성 할당인지 또는 여러 번 발생할 수 있는지 확실하지 않습니다.

한 번만 초기화하면 생성자에 두 개의 반복자 벡터 생성자를 사용할 수 있습니다.

Foo::Foo(double* w, int len) : w_(w, w + len) { }

그렇지 않으면 이전에 제안한대로 assign을 사용하십시오.

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

1
제 경우에는 과제가 반복적으로 발생합니다.
Frank

12

배열의 크기를 자동으로 '학습'할 수 있습니다.

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

위와 같이 인터페이스를 set_data로 변경할 수 있기를 바랍니다. 여전히 C 스타일 배열을 첫 번째 인수로 사용합니다. 그것은 단지 참조로 가져옵니다.


작동 원리

[업데이트 : 크기 학습에 대한보다 포괄적 인 논의는 여기 를 참조 하십시오 ]

더 일반적인 해결책은 다음과 같습니다.

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

이것은 배열이 배열에 대한 참조로 전달되기 때문에 작동합니다. C / C ++에서는 배열을 함수로 전달할 수 없으며 대신 포인터로 쇠약 해지고 크기를 잃게됩니다. 그러나 C ++에서는 배열에 대한 참조를 전달할 수 있습니다 .

참조로 배열을 전달하려면 형식이 정확히 일치해야합니다. 배열의 크기는 해당 유형의 일부입니다. 즉, 템플릿 매개 변수 N을 사용하여 크기를 알 수 있습니다.

벡터를 반환하는이 함수를 사용하는 것이 더 간단 할 수도 있습니다. 적절한 컴파일러 최적화가 적용되면 보이는 것보다 빠릅니다.

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

1
마지막 샘플 return { begin(source_array), end(source_array) };에서도 가능합니다
MM

12

파벨은 가까웠지만 ac 스타일 배열에서 순차적 컨테이너를 초기화하는 더 간단하고 우아한 솔루션이 있습니다.

귀하의 경우 :

w_ (array, std::end(array))
  • 배열은 배열의 시작 부분에 대한 포인터를 얻습니다 (이름을 잡지 못함).
  • std :: end (array)는 배열 끝에 반복자를 가져옵니다.

1
C ++의 포함 / 버전에는 무엇이 필요합니까?
Vlad

1
이것은 적어도 c ++ 98 이상의 std :: vector 생성자 중 하나입니다. '범위 생성자'라고합니다. cplusplus.com/reference/vector/vector/vector 사용해보십시오.
Mugurel

1
더 독립적 인 버전은 다음과 같습니다. w_ (std :: begin (array), std :: end (array)); (앞으로 C ++ 컨테이너의 C 배열을 변경할 수 있습니다).
앤드류 로마노프

13
당신이 실수를 가지고있는 경우에만 작동합니다 array(일반적으로 전역 또는 로컬 (현재 함수로 선언) 배열에서 복사한다는 것을 의미합니다). OP의 경우 포인터와 길이를 수신하고 길이에 템플릿이 없으므로 크기가 지정된 배열이나 다른 것에 대한 포인터를 수신하도록 변경할 수 std::end없으므로 작동하지 않습니다.
ShadowRanger

2
vector오버로드하지 않으므로 operator()컴파일되지 않습니다. std::end포인터에서 호출되는 것도 사용되지 않습니다 (질문은 포인터에서 벡터를 할당하고 별도의 길이 변수를 묻습니다). 제안하려는 내용에 대한 자세한 내용을 보여 주면 답변이 향상 될 것입니다.
MM

2

빠른 일반적인 답변 :

std::vector<double> vec(carray,carray+carray_size); 

또는 특정 질문 :

std::vector<double> w_(w,w+len); 

위의 기준 : 포인터를 반복자로 취급 할 수 있다는 것을 잊지 마십시오 .


0

std::vector<double>::assign코드적기 때문에 갈 길 입니다. 그러나 실제로 어떻게 작동합니까? 크기를 조정 한 다음 복사하지 않습니까? STL의 MS 구현에서는 정확히 사용하고 있습니다.

귀하의를 (재) 초기화하는 더 빠른 방법 이 없을까 걱정 std::vector됩니다.


벡터와 배열간에 데이터를 공유하면 어떻게됩니까? 이 경우 아무 것도 복사해야합니까?
Vlad

그게 답입니까, 질문입니까? 기존 답변에 무엇을 가져 옵니까?
Jean-François Fabre

@ Jean-FrançoisFabre 그리고 당신의 의견은 무엇을 가져 옵니까? ;) 사실, 그것은 오래 전에 주어진 나쁜 대답입니다.
Janusz Lenar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.