cách khởi tạo 'const std :: vector <T>' giống như mảng ac


79

Có cách nào hay để tạo và khởi tạo một const std::vector<const T>like const T a[] = { ... }cho một số giá trị cố định (và nhỏ) không?
Tôi cần thường xuyên gọi một hàm mong đợi a vector<T>, nhưng những giá trị này sẽ không bao giờ thay đổi trong trường hợp của tôi.

Về nguyên tắc, tôi nghĩ về một cái gì đó như

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

vì v sẽ không được sử dụng bên ngoài đơn vị biên dịch này.

Câu trả lời:


61

Bạn phải đợi C ++ 0x hoặc sử dụng thứ gì đó như Boost.Assign để làm điều đó.

ví dụ:

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

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

cho C ++ 11:

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

1
"Phải" có thể hơi mạnh; phương pháp tiếp cận hai bước cũng có thể hoạt động. Mặc dù cách tiếp cận tăng cường chắc chắn là tốt.
janm

Geussing ý bạn là: #include <boost / gán / std / vector.hpp>
Zac

1
@ Jason: định nghĩa boost.assign templated quá tải toán tử + = và nhà điều hành, trên vector <T>
Ferruccio

nhưng làm thế nào để bạn chặn từng giá trị dấu phẩy?
Jason S

41
12345? Tôi đã có sự kết hợp tương tự trên hành lý của mình!
JRG

39

Nếu bạn đang hỏi làm thế nào để khởi tạo một vectơ const để nó có nội dung thú vị, thì câu trả lời có thể là sử dụng hàm tạo bản sao. Đầu tiên, bạn điền một cách chăm chỉ vào một vectơ, sau đó bạn tạo vectơ const mới từ nó. Hoặc bạn có thể sử dụng vector<InputIterator>(InputIterator, InputIterator)mẫu phương thức khởi tạo để khởi tạo từ một số loại vùng chứa hoặc mảng khác. Nếu một mảng, thì mảng đó có thể đã được xác định bằng danh sách khởi tạo.

Một cái gì đó như thế này hy vọng gần với những gì bạn muốn:

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

Nếu bạn đang hỏi làm thế nào để truyền một vectơ const vào một hàm nhận một vectơ thì câu trả lời là:

  • bạn không thể, vì hàm có thể thay đổi vectơ và đối tượng / tham chiếu của bạn là const. Tạo một bản sao không phải const của bản gốc và chuyển nó vào.

hoặc là

  • sử dụng const_castđể loại bỏ hằng số để chuyển nó vào một hàm nhận một vectơ không phải const nhưng mà bạn tình cờ biết sẽ không sửa đổi vectơ.

Cái thứ hai là một trong những thứ, hoàn toàn đúng, khiến bất cứ ai nhìn thấy nó sẽ đưa ra nhận xét về kính bảo hộ và thực tế là họ không làm gì cả. Nó chính xác const_castlà để làm gì , nhưng có một lập luận hợp lý mạnh mẽ nói rằng nếu bạn cần const_cast, bạn đã thua.

Thực hiện cả hai điều đó (tạo một vectơ const từ một vectơ không phải const bằng hàm tạo bản sao, và sau đó loại bỏ hằng số) chắc chắn là sai - bạn nên sử dụng một vectơ không phải const. Vì vậy, hãy chọn nhiều nhất một trong số này để làm ...

[ Chỉnh sửa: chỉ nhận thấy rằng bạn đang nói về sự khác biệt giữa vector<T>const vector<const T>. Thật không may trong STL, vector<const T>vector<T>là các loại hoàn toàn không liên quan, và cách duy nhất để chuyển đổi giữa chúng là bằng cách sao chép. Đây là sự khác biệt giữa vectơ và mảng - a T**có thể được chuyển đổi âm thầm và an toàn thành const T *const *]


Nếu tôi không muốn sao chép t1, t2, t3, làm thế nào tôi có thể làm điều này tốt nhất? Đây là giải pháp duy nhất / tốt nhất mà tôi tìm thấy cho đến nay: <code> const int * p = & ra [0]; vectơ <const int *> v; for (int i = 0; i <size; ++ i, ++ p) v.push_back (p); </code>
AudioDroid

@AudioDroid: vâng, không có cách nào để đưa bất cứ thứ gì vào một vectơ mà không sao chép nó (hoặc di chuyển nó, trong C ++ 11), vì vậy cái gần nhất bạn sẽ nhận được là một vectơ con trỏ (hoặc con trỏ thông minh). Nhưng nếu bạn đã có ba đối tượng của mình trong một mảng, thì thông thường không có ích gì khi tạo một vectơ con trỏ tới chúng: chỉ cần sử dụng mảng cho bất cứ thứ gì bạn sẽ sử dụng vectơ.
Steve Jessop

15

Cách ngắn và bẩn (tương tự như của 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"));
}

Bây giờ, C ++ 11 có danh sách trình khởi tạo, vì vậy bạn không cần phải làm theo cách đó hoặc thậm chí sử dụng Boost. Tuy nhiên, như một ví dụ, bạn có thể làm điều trên trong C ++ 11 hiệu quả hơn như sau:

    #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;
        }
    }

Tuy nhiên, nó vẫn không hiệu quả bằng việc sử dụng danh sách trình khởi tạo C ++ 11 vì không có operator=(vlist_of&&)định nghĩa cho vector.

Cách của tjohns20 được sửa đổi như sau có thể là một c ++ 11 tốt hơn 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;
    }
    
}

xấu xa, nhưng hữu ích để thử nghiệm, v.v. ++
Eli Bendersky

12

Như những người khác đã nói, bạn không thể init một vectơ giống như cách bạn có thể bắt một mảng kiểu C, trừ khi bạn cung cấp con trỏ cho một mảng nguồn. Nhưng trong trường hợp đó, nếu vectơ của bạn là một hằng số toàn cục, tại sao không chỉ sử dụng một mảng kiểu C cũ để thay thế?

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

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

Bạn thậm chí có thể sử dụng thuật toán STL đối với mảng này, giống như cách bạn sử dụng thuật toán đối với vectơ const ...

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

4
Đó là một điểm tốt, nhưng anh ta đang chuyển nó cho một hàm mong đợi một vectơ. Anh ta có thể không sửa đổi được chức năng đó.
Ferruccio

Điều này đòi hỏi phải theo dõi kích thước của vectơ bên ngoài, đây là một khó khăn khi xử lý nhiều vectơ. Nếu không, giải pháp này sẽ là giải pháp tốt nhất vì nó chỉ yêu cầu một bản sao của các số nguyên trong bộ nhớ.
MasterHD

6

Bạn có thể thực hiện theo hai bước:

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])));
}

Có lẽ không đẹp như bạn có thể thích, nhưng có chức năng.


5

Làm thế nào về:

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

Đừng bận tâm, "Steve Jessop" đã chỉ ra cách tiếp cận này rồi.
opal

3

Câu hỏi cũ, nhưng hôm nay tôi lại gặp phải vấn đề tương tự, đây là cách tiếp cận được chấp nhận nhất cho mục đích của tôi:

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;
}

Ví dụ để tránh sao chép quá nhiều:

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

Nếu chúng đều giống nhau, bạn chỉ có thể làm

vector<T> vec(num_items, item);

nhưng tôi cho rằng chúng không phải - trong trường hợp đó, cách gọn gàng nhất có lẽ là:

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

C ++ 0x sẽ cho phép bạn sử dụng danh sách khởi tạo chính xác theo cách bạn đang nghĩ, nhưng điều đó không tốt ngay bây giờ, rất tiếc.


0

Dựa trên phản hồi của Shadow2531, tôi đang sử dụng lớp này để khởi tạo vectơ, mà không thực sự kế thừa từ vectơ std :: như giải pháp của Shadow đã làm

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;
};

Sử dụng:

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

So với giải pháp của Steve Jessop, nó tạo ra nhiều mã hơn, nhưng nếu việc tạo mảng không quan trọng về hiệu suất, tôi thấy đây là một cách hay để khởi tạo một mảng trong một dòng


0

Không chắc liệu tôi có hiểu bạn đúng không. Tôi hiểu câu hỏi của bạn như sau: bạn muốn khởi tạo một vectơ với một số lượng lớn các phần tử. Có gì sai với việc sử dụng push_back()trên vector? :-)

Nếu bạn biết số lượng phần tử sẽ được lưu trữ (hoặc chắc chắn rằng nó sẽ lưu trữ ít hơn lũy thừa tiếp theo là 2), bạn có thể làm điều này, nếu bạn có vectơ con trỏ kiểu X (chỉ hoạt động với con trỏ):

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];

Hãy cẩn thận khi thêm nhiều hơn sức mạnh tiếp theo của 2 phần tử, ngay cả khi đang sử dụng push_back(). Con trỏ đến v.begin()sau đó sẽ không hợp lệ.


Có rất nhiều vấn đề với mã này. Bạn không thể chỉ gọi vector :: Reserve () và sau đó bắt đầu thao tác với con trỏ và phụ thuộc vào các chi tiết triển khai ma thuật như lũy thừa của hai. Trình lặp được chuyển đổi thành con trỏ? Còn về vector :: size ()? Trên trình biên dịch và thư viện STL nào mà điều này thậm chí biên dịch?
janm

Tôi đồng ý rằng đây là mã rất xấu. Tốt nhất, bạn nên quên việc sử dụng con trỏ / vòng lặp và dự phòng cho push_back
paercebal

Không, đó là mã C ++ hoàn toàn hợp pháp. STL đảm bảo rằng điều này sẽ hoạt động: vector sẽ phân bổ không gian liên tiếp cho ít nhất 2 ^ n phần tử. Sau khi tất cả 2 ^ n phần tử được phép phân bổ lại, cung cấp cho bạn một không gian mới đằng sau vector :: begin (). Xin lỗi, nó có thể là hacky, nhưng đó là tiêu chuẩn. :-)
mstrobl 23/10/08

1
... tuy nhiên, tôi hoàn toàn không tin rằng việc sử dụng đảm bảo bộ nhớ liền kề để thêm các giá trị mới bằng con trỏ là tiêu chuẩn - đối với người mới bắt đầu, vectơ không có cơ hội cập nhật kích thước của chính nó, vì vậy tôi khá chắc chắn về kết quả của mã này là không xác định hoặc là vectơ có kích thước 0.
Steve Jessop

1
Tất nhiên bạn có thể tận dụng không gian liền kề trong vector; tiêu chuẩn đảm bảo điều đó. Tuy nhiên, gọi Reserve () là không đủ để làm điều đó; bạn phải thêm các phần tử sao cho kích thước () là chính xác. Bạn có thể thay đổi mã để bạn phân bổ số phần tử chính xác ....
janm 24/10/08
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.