статическая константа 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());

Однако я получаю следующую ошибку:

запрос члена «начало» в масштабах неклассового типа «int[10]» запрос члена «конец» в масштабах неклассового типа «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++ не могут иметь членов. Однако вы ищете статическую диспетчеризацию, и она отлично работает с разрешением перегрузки по типу параметра. Таким образом, С++ 11 предоставляет std::begin и std::end функции, не являющиеся членами. (Об этом уже упоминалось.)

Привыкайте к лучшей практике вызова функций, не являющихся членами, следующим образом (это действительно помогает при написании общего кода шаблона):

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

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

Это будет работать правильно, независимо от типа контейнера zooms, и будет использовать ADL (поиск в зависимости от аргумента, иногда называемый поиском Кенига) для поиска реализаций 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
Почему все ответы предоставляют решение С++ 11 с std::begin и std::end, когда вы хотите напрямую использовать list-initialization для вектора (и в первую очередь устранить необходимость в массиве)? - person David Rodríguez - dribeas; 07.12.2012
comment
Да, все остальные эффекты полностью отрицательные, а не только в основном отрицательные. @BenVoigt: Возможно, но нет никаких признаков того, что ОП нуждается в такой поддержке. - person Puppy; 07.12.2012
comment
@DavidRodríguez-dribeas: Мое решение не требует C++11. И я искал лучший способ инициализировать вектор из какого-либо другого контейнера, а не лучший способ инициализировать вектор из буквального списка. - person Ben Voigt; 07.12.2012
comment
Я думаю, что предложение с началом и концом функций шаблона очень хорошее. Спасибо! - person Cristi; 09.12.2012

Здесь я бы придерживался разных подходов в зависимости от того, есть ли у вас доступ к С++ 11 или нет.

В C++03 я бы использовал обычный массив (поскольку он константный) и, вероятно, даже не в классе, а в частном пространстве имен в файле реализации (поскольку оно является закрытым, предполагая, что только одна единица перевода имеет определения для членов 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();

В С++ 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>, вы можете использовать list-initialization

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