static const std::vector

Пиша програма за преглед на изображения с Qt. Опитвам се да направя следното в заглавния файл:

class ImageModel
{


private:
    const static std::vector<int> mZoomLevels;

}

в изходния файл:

int zooms[] = {1,2,3,4,5,6,7,8,9,10};
const std::vector<int> mZoomLevels(zooms.begin(),zooms.end());

Въпреки това получавам следната грешка:

заявка за член 'begin' в zooms, който е от некласов тип 'int[10]' заявка за член 'end' в zooms, който е от некласов тип 'int[10]'

Някой знае ли как да инициализира този статичен константен частен член?


person Cristi    schedule 07.12.2012    source източник
comment
Това трябва ли да е const std::vector<int> ImageModel::mZoomLevels(...)?   -  person Ben Voigt    schedule 07.12.2012


Отговори (5)


Обикновените масиви нямат функции-членове. Вярвам, че търсите това:

int zooms[] = {1,2,3,4,5,6,7,8,9,10};
const std::vector ImageModel::mZoomLevels(zooms, zooms + 10);
person Angew is no longer proud of SO    schedule 07.12.2012
comment
Благодаря ви за бързият отговор! - person Cristi; 07.12.2012

Масивите нямат begin и end членове. Можете да използвате име на масив за begin и име на масив плюс дължина за края:

const std::vector mZoomLevels(zooms, zooms+10);

В C++11 можете да използвате std::begin и std::end, като това:

const std::vector mZoomLevels(std::begin(zooms), std::end(zooms));

И в двата случая е добра идея да декларирате вашия zooms масив файл-статичен или да го скриете в пространство от имена, за да сте сигурни, че името му не "замърсява" глобалното пространство от имена.

person Sergey Kalinichenko    schedule 07.12.2012
comment
Ако имате C++11 и искате да инициализирате вектора, няма нужда да създавате масива, просто използвайте list-initialization: const std::vector<int> ImageModel::mZoomLevels{1,2,3,4...}; --less код и премахвате ненужен масив. Това е, ако наистина искате да използвате std::vector<int>... в което не съм много сигурен... - person David Rodríguez - dribeas; 07.12.2012

zooms е масив в стил C, който няма членове и методи, т.е. zooms.begin и zooms.end нямат смисъл. Ако използвате C++11 съвместим компилатор, опитайте std::begin(zooms) и std::end(zooms)

person Arne Mertz    schedule 07.12.2012

Нормалните C++ масиви не могат да имат членове. Вие обаче търсите статично изпращане и това работи добре с резолюция на претоварване по тип параметър. Така че C++11 предоставя std::begin и std::end функции, които не са членове. (Това вече беше споменато.)

Свикнете с най-добрата практика за извикване на функции, които не са членове, както следва (това наистина помага, докато пишете общ шаблонен код):

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

const std::vector mZoomLevels(begin(zooms), end(zooms));

Това ще работи правилно, без значение какъв тип е контейнерът zooms, и ще се възползва от ADL (зависимо от аргументи търсене, понякога наричано търсене на Koenig), за да намери реализации на begin и end в едно и също пространство от имена, ако zooms има някакъв потребителски тип клас.

Между другото, std::begin и std::end се предоставят от C++11, но можете да напишете свои собствени достатъчно лесно за по-ранни версии:

template <typename T, size_t N>
T* begin( T (&a)[N] ) { return a; }

template <typename T, size_t N>
T* end( T (&a)[N] ) { return a + N; }
person Ben Voigt    schedule 07.12.2012
comment
Защо using? Тук няма нужда от ADL. - person Puppy; 07.12.2012
comment
@DeadMG: Защо не using? Ако придобие навика да го прави правилно, той няма изведнъж да има проблеми, когато го използва в шаблон. - person Ben Voigt; 07.12.2012
comment
Един шаблон не би променил нищо. Той го извиква на естествен масив - std::begin и std::end винаги ще бъдат избрани. - person Puppy; 07.12.2012
comment
@DeadMG: Помислете за случая, когато zooms е параметър, а не глобална променлива, а типът на параметъра е шаблонизиран, за да позволи всеки тип контейнер. Кодът във въпроса е SSCE, не непременно толкова сложен, колкото реалната ситуация, която вероятно ще се срещне. - person Ben Voigt; 07.12.2012
comment
@DeadMG: Защо using, когато няма нужда от ADL? Директивата using въвежда символ в обхват, който е почти ортогонален на ADL... признава се, че имате нужда от него, ако искате поддръжка на ADL, но това не е единствената цел от декларацията за използване. - person David Rodríguez - dribeas; 07.12.2012
comment
Защо всички отговори предоставят решение на C++11 с std::begin и std::end, когато искате да използвате списък-инициализация директно върху вектора (и да премахнете необходимостта от масива на първо място)? - person David Rodríguez - dribeas; 07.12.2012
comment
Да, всички други ефекти са изцяло отрицателни, вместо само предимно отрицателни. @BenVoigt: Може би, но няма индикация, че OP се нуждае от тази поддръжка. - person Puppy; 07.12.2012
comment
@DavidRodríguez-dribeas: Моето решение не изисква C++11. И исках да потърся най-добрия начин за инициализиране на вектор от някакъв друг контейнер, за разлика от най-добрия начин за инициализиране на вектор от литерал списък. - person Ben Voigt; 07.12.2012
comment
Мисля, че предложението с шаблонните функции начало и край е много добро. Благодаря ви! - person Cristi; 09.12.2012

Тук бих следвал различни подходи в зависимост от това дали имате достъп до C++11 или не.

В C++03 бих използвал обикновен масив (тъй като е const) и вероятно дори не в класа, а в частно пространство от имена във файла за изпълнение (тъй като е частно, ако приемем, че само една транслационна единица има дефиниции за членове на ImageModel).

// cpp
namespace {
   static int gZoomLevels[] = { 1, 2, ... };
}

Ако наистина искате да продължите да използвате подхода std::vector<int>, бих създал помощна функция в единицата за превод, която дефинира члена, и ще я използвам за създаване на std::vector, без да създавам различна променлива със статична продължителност:

namespace {
   static std::vector<int> chooseANameForInitializer() {
       int data[] = { 1, 2, 3 };
       return std::vector<int>( data, data + (sizeof data/sizeof *data) );
   }
}
const std::vector<int> ImageModel::mZoomLevels = chooseANameForInitializer();

В C++11 бих използвал std::array<int,...> вместо това, тъй като това избягва динамичното разпределение и цената на допълнителната индиректност. Разбира се, това не е голяма печалба, но няма смисъл да имате std::vector<int>, когато не се нуждаете от функциите, които предлага.

class ImageModel
{
private:
    static const std::array<int,10> mZoomLevels;  
};
// cpp:
const std::array<int,10> ImageModel::mZoomLevels = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Отново, ако настоявате да имате std::vector<int>, тогава можете да използвате списък-инициализация

const std::vector<int> ImageModel::mZoomLevels{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
person David Rodríguez - dribeas    schedule 07.12.2012