ac 배열과 같이 'const std :: vector <T>'를 초기화하는 방법


79

고정 된 (그리고 적은) 수의 값에 const std::vector<const T>좋아요 를 만들고 초기화하는 우아한 방법이 const T a[] = { ... }있습니까?
를 예상하는 함수를 자주 호출해야 vector<T>하지만이 값은 제 경우에는 절대 변경되지 않습니다.

원칙적으로 나는 다음과 같은 것을 생각했다.

namespace {
  const std::vector<const T> v(??);
}

v는이 컴파일 단위 외부에서 사용되지 않기 때문입니다.

답변:


61

이를 위해 C ++ 0x를 기다리거나 Boost.Assign 과 같은 것을 사용해야 합니다.

예 :

#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope

vector<int> v;
v += 1,2,3,4,5;

C ++ 11의 경우 :

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };

1
"Have to"는 약간 강할 수 있습니다. 2 단계 접근법도 작동 할 수 있습니다. 부스트 접근 방식은 확실히 좋습니다.
janm

의미 : #include <boost / assign / std / vector.hpp>
Zac

1
@ 제이슨 : ON 연산자 + = 오퍼레이터의 boost.assign 정의 템플릿 과부하 벡터 <T>
페루 치오

하지만 각 쉼표 값을 어떻게 가로 채나요?
Jason S

41
12345? 내 짐에 같은 조합이 있어요!
JRG 2011 년

39

흥미로운 내용을 갖도록 const 벡터를 초기화하는 방법을 묻는 경우 대답은 아마도 복사 생성자를 사용하는 것입니다. 먼저 벡터를 힘들게 채운 다음 여기에서 새로운 const 벡터를 만듭니다. 또는 vector<InputIterator>(InputIterator, InputIterator)생성자 템플릿을 사용하여 다른 종류의 컨테이너 또는 배열에서 초기화 할 수 있습니다 . 배열이라면 초기화 목록으로 정의되었을 수 있습니다.

이와 같은 것이 당신이 원하는 것에 가깝기를 바랍니다.

const T ra[3] = {t1, t2, t3};
const vector<const T> v(ra, ra+3);

벡터를 취하는 함수에 const 벡터를 전달하는 방법을 묻는다면 답은 다음 중 하나입니다.

  • 함수가 벡터를 변경할 수 있고 객체 / 참조가 const이므로 할 수 없습니다. 원본의 상수가 아닌 복사본을 만들어 전달합니다.

또는

  • const_cast상수가 아닌 벡터를 사용하는 함수로 전달하기 위해 상수를 제거하는 데 사용 합니다.하지만 알고있는 것은 벡터를 수정하지 않습니다.

후자는 고글을 보는 사람이 고글에 대해 의견을 말하고 아무것도하지 않는다는 사실을 알리는 것 중 하나입니다. 그것은 정확히 무엇 const_cast을위한 것이지만, 만약 당신이 필요하다면 const_cast, 당신은 이미 패배 했다는 합리적으로 강력한 주장이 있습니다 .

이 두 가지 작업을 모두 수행하는 것은 (복사 생성자로 상수가 아닌 벡터에서 const 벡터를 만든 다음 상수를 캐스트하는 것) 확실히 잘못된 것입니다. 방금 비 상수 벡터를 사용 했어야합니다. 따라서이 중 하나만 선택하여 수행하십시오.

[ 편집 :vector<T> 과 (와) 의 차이점에 대해 이야기하고 있음을 확인했습니다 const vector<const T>. 불행히도 STL에서 vector<const T>vector<T>전혀 관계가없는 유형, 그리고 그들 사이에 변환 할 수있는 유일한 방법은 복사하는 것입니다. 이것은 벡터와 배열의 차이입니다. a는 T**조용하고 안전하게 const T *const *] 로 변환 될 수 있습니다 .


t1, t2, t3을 복사하고 싶지 않다면 어떻게하면 최선을 다할 수 있습니까? 이것이 내가 지금까지 찾은 유일한 / 최고의 솔루션입니다. <code> const int * p = & ra [0]; 벡터 <const int *> v; for (int i = 0; i <size; ++ i, ++ p) v.push_back (p); </ code>
AudioDroid

@AudioDroid : 예, 벡터를 복사 (또는 C ++ 11에서 이동)하지 않고는 벡터에 아무것도 넣을 수 없으므로 가장 가까운 것은 포인터 (또는 스마트 포인터)의 벡터입니다. 그러나 이미 배열에 세 개의 개체가있는 경우 일반적으로 개체에 대한 포인터 벡터를 생성 할 필요가 없습니다. 벡터를 사용할 목적으로 배열을 사용하십시오.
Steve Jessop

15

짧고 더러운 방법 (Boost와 유사 list_of())

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;

template <typename T>
struct vlist_of : public vector<T> {
    vlist_of(const T& t) {
        (*this)(t);
    }
    vlist_of& operator()(const T& t) {
        this->push_back(t);
        return *this;
    }
};

int main() {
    const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
    copy(v.begin(), v.end(), ostream_iterator<int>(cout, "\n"));
}

이제 C ++ 11에는 이니셜 라이저 목록이 있으므로 그렇게하거나 Boost를 사용할 필요가 없습니다. 그러나 예를 들어 C ++ 11에서 다음과 같이보다 효율적으로 위의 작업을 수행 할 수 있습니다.

    #include <iostream>
    #include <vector>
    #include <utility>
    #include <ostream>
    using namespace std;

    template <typename T>
    struct vlist_of : public vector<T> {
        vlist_of(T&& t) {
            (*this)(move(t));
        }
        vlist_of& operator()(T&& t) {
            this->push_back(move(t));
            return *this;
        }
    };

    int main() {
        const vector<int> v = vlist_of<int>(1)(2)(3)(4)(5);
        for (const auto& i: v) {
            cout << i << endl;
        }
    }

그러나 operator=(vlist_of&&)벡터에 대한 정의 가 없기 때문에 C ++ 11 이니셜 라이저 목록을 사용하는 것만 큼 효율적이지 않습니다 .

다음과 같이 수정 된 tjohns20의 방법이 더 나은 C ++ 11 일 수 있습니다 vlist_of.

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

template <typename T>
class vlist_of {
    public:
        vlist_of(T&& r) {
            (*this)(move(r));
        }
        vlist_of& operator()(T&& r) {
            v.push_back(move(r));
            return *this;
        }
        vector<T>&& operator()() {
            return move(v);
        }
    private:
        vector<T> v;
    
};

int main() {
    const auto v = vlist_of<int>(1)(2)(3)(4)(5)();
    for (const auto& i : v) {
        cout << i << endl;
    }
    
}

사악하지만 테스트 등에 유용합니다. ++
Eli Bendersky

12

다른 사람들이 말했듯이 소스 배열에 대한 포인터를 제공하지 않는 한 C 스타일 배열을 초기화하는 것과 같은 방식으로 벡터를 초기화 할 수 없습니다. 그러나이 경우 벡터가 전역 상수 인 경우 기존 C 스타일 배열을 대신 사용하지 않는 이유는 무엇입니까?

const int MyInts[] = {
1, 2, 3, 4, 5};

const size_t NumMyInts = sizeof(MyInts)/sizeof(MyInts[0]);

const 벡터에 대해 알고리즘을 사용하는 것과 같은 방식으로이 배열에 대해 STL 알고리즘을 사용할 수도 있습니다.

const int* myInt = std::find( &MyInts[0], &MyInts[NumMyInts], 3);

4
그것은 좋은 지적이지만 그는 벡터를 기대하는 함수에 그것을 전달하고 있습니다. 그는 해당 기능을 수정할 수 없습니다.
Ferruccio

이를 위해서는 벡터의 크기를 외부 적으로 추적해야하는데, 이는 많은 벡터를 다룰 때 고통 스럽습니다. 그렇지 않으면 메모리에 정수 사본이 하나만 필요하므로이 솔루션이 가장 좋습니다.
MasterHD

6

두 단계로 수행 할 수 있습니다.

namespace {
    const T s_actual_array[] = { ... };
    const std::vector<const T> s_blah(s_actual_array,
        s_actual_array + (sizeof(s_actual_array) / sizeof(s_actual_array[0])));
}

아마도 당신이 좋아하는 것만 큼 아름답지는 않지만 기능적입니다.


5

어때 :

int ar[]={1,2,3,4,5,6};
const int TotalItems = sizeof(ar)/sizeof(ar[0]);
std::vector<int> v(ar, ar+TotalItems);

"Steve Jessop"은 이미이 접근 방식을 지적했습니다.
opal

3

오래된 질문이지만 오늘 같은 문제가 발생했습니다. 여기 내 목적에 가장 적합한 접근 방식이 있습니다.

vector<int> initVector(void)
{
    vector<int> initializer;
    initializer.push_back(10);
    initializer.push_back(13);
    initializer.push_back(3);
    return intializer;
}

int main()
{
    const vector<int> a = initVector();
    return 0;
}

과도한 복사를 방지하기위한 예 :

vector<int> & initVector(void)
{
    static vector<int> initializer;
    if(initializer.empty())
    {
        initializer.push_back(10);
        initializer.push_back(13);
        initializer.push_back(3);
    }
    return intializer;
}

int main()
{
    const vector<int> & a = initVector();
    return 0;
}

0

모두 똑같 으면 그냥 할 수 있습니다

vector<T> vec(num_items, item);

그러나 나는 그들이 그렇지 않다고 가정합니다.이 경우 가장 좋은 방법은 아마도 다음과 같습니다.

vector<T> vec(num_items);
vec[0] = 15;
vec[1] = 5;
...

C ++ 0x를 사용하면 이니셜 라이저 목록을 정확히 생각하는 방식으로 사용할 수 있지만 불행히도 지금은 그다지 좋지 않습니다.


0

Shadow2531의 응답을 기반으로, Shadow의 솔루션처럼 std :: vector에서 실제로 상속하지 않고이 클래스를 사용하여 벡터를 초기화하고 있습니다.

template <typename T>
class vector_init
{
public:
    vector_init(const T& val)
    {
        vec.push_back(val);
    }
    inline vector_init& operator()(T val)
    {
        vec.push_back(val);
        return *this;
    }
    inline std::vector<T> end()
    {
        return vec;
    }
private:
    std::vector<T> vec;
};

용법:

std::vector<int> testVec = vector_init<int>(1)(2)(3)(4)(5).end();

Steve Jessop의 솔루션에 비해 훨씬 더 많은 코드가 생성되지만 배열 생성이 성능에 중요하지 않은 경우 한 줄로 배열을 초기화하는 좋은 방법이라고 생각합니다.


0

내가 당신을 옳게 이해했는지 확실하지 않습니다. 나는 당신의 질문을 다음과 같이 이해합니다 : 당신은 많은 수의 요소로 벡터를 초기화하고 싶습니다. push_back()벡터 에서 사용 하는 것이 잘못된 것은 무엇입니까 ? :-)

저장할 요소의 수를 알고 있거나 다음 2의 거듭 제곱보다 적게 저장 될 것이라고 확신하는 경우 X 유형의 포인터 벡터가있는 경우 (포인터에서만 작동) 이렇게 할 수 있습니다.

std::vector< X* > v;
v.reserve(num_elems);
X* p = v.begin();
for (int count = 0; count < num_elems; count++)
   p[count] = some_source[count];

를 사용하더라도 2 개 요소의 다음 거듭 제곱 이상을 추가하지 않도록주의하십시오 push_back(). v.begin()그러면 포인터 가 무효화됩니다.


이 코드에는 많은 문제가 있습니다. vector :: reserve ()를 호출 한 다음 포인터 조작을 시작할 수 없으며 2의 거듭 제곱과 같은 마법 구현 세부 정보에 따라 다릅니다. 반복자가 포인터로 변환 되었습니까? vector :: size ()는 어떻습니까? 어떤 컴파일러와 STL 라이브러리에서 이것이 컴파일됩니까?
janm

나는 이것이 매우 나쁜 코드라는 데 동의합니다. A 가장 좋은 점은 포인터 / 반복자 사용을 잊어 버리고 push_back으로 폴백해야한다는 것입니다.
paercebal

아니요, 완벽하게 합법적 인 C ++ 코드입니다. STL은 이것이 작동 함을 보장합니다. 벡터는 최소 2 ^ n 개의 요소에 연속적인 공간을 할당합니다. 모든 2 ^ n 요소 벡터는 재 할당이 허용되어 vector :: begin () 뒤에 새 공간을 제공합니다. 죄송합니다. 해킹이 될 수도 있지만 표준입니다. :-)
mstrobl

1
... 그러나 포인터로 새 값을 추가하기 위해 연속 메모리 보장을 사용하는 것이 표준이라고 확신하지 못합니다. 우선 벡터는 자체 크기를 업데이트 할 기회가 없으므로 결과가 확실합니다. 이 코드는 정의되지 않았거나 크기가 0 인 벡터입니다.
Steve Jessop

1
물론 벡터에서 연속적인 공간을 활용할 수 있습니다. 표준은 그것을 보장합니다. 그러나, reserve () 호출만으로는 충분하지 않습니다. size ()가 정확하도록 요소를 추가해야합니다. 올바른 수의 요소를 할당하도록 코드를 변경할 수 있습니다. ....
janm
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.