Инициализировать массив const в инициализаторе класса в С++

У меня есть следующий класс в С++:

class a {
    const int b[2];
    // other stuff follows

    // and here the constructor
    a(void);
}

Вопрос в том, как инициализировать b в списке инициализации, учитывая, что я не могу инициализировать его внутри тела функции конструктора, поскольку b является const?

Это не работает:

a::a(void) : 
    b([2,3])
{
     // other initialization stuff
}

Edit: В данном случае я могу иметь разные значения для b для разных экземпляров, но значения, как известно, являются константами для времени жизни экземпляра.

Ответ 1

Как и другие, ISO С++ этого не поддерживает. Но вы можете обойти это. Просто используйте std::vector.

int* a = new int[N];
// fill a

class C {
  const std::vector<int> v;
public:
  C():v(a, a+N) {}
};

Ответ 2

С С++ 11 ответ на этот вопрос теперь изменился, и вы действительно можете:

struct a {
    const int b[2];
    // other bits follow

    // and here the constructor
    a();
};

a::a() :
    b{2,3}
{
     // other constructor work
}

int main() {
 a a;
}

Ответ 3

В текущем стандарте это невозможно. Я считаю, что вы сможете сделать это в С++ 0x, используя списки инициализаторов (см. Краткий обзор С++ 0x, by Bjarne Stroustrup, для получения дополнительной информации об списках инициализаторов и других полезных функциях С++ 0x).

Ответ 4

std::vector использует кучу. Геэз, какая трата, которая была бы только ради проверки работоспособности const. Точка std::vector - динамический рост во время выполнения, а не старая проверка синтаксиса, которая должна выполняться во время компиляции. Если вы не собираетесь вырасти, создайте класс, чтобы обернуть нормальный массив.

#include <stdio.h>


template <class Type, size_t MaxLength>
class ConstFixedSizeArrayFiller {
private:
    size_t length;

public:
    ConstFixedSizeArrayFiller() : length(0) {
    }

    virtual ~ConstFixedSizeArrayFiller() {
    }

    virtual void Fill(Type *array) = 0;

protected:
    void add_element(Type *array, const Type & element)
    {
        if(length >= MaxLength) {
            // todo: throw more appropriate out-of-bounds exception
            throw 0;
        }
        array[length] = element;
        length++;
    }
};


template <class Type, size_t Length>
class ConstFixedSizeArray {
private:
    Type array[Length];

public:
    explicit ConstFixedSizeArray(
        ConstFixedSizeArrayFiller<Type, Length> & filler
    ) {
        filler.Fill(array);
    }

    const Type *Array() const {
        return array;
    }

    size_t ArrayLength() const {
        return Length;
    }
};


class a {
private:
    class b_filler : public ConstFixedSizeArrayFiller<int, 2> {
    public:
        virtual ~b_filler() {
        }

        virtual void Fill(int *array) {
            add_element(array, 87);
            add_element(array, 96);
        }
    };

    const ConstFixedSizeArray<int, 2> b;

public:
    a(void) : b(b_filler()) {
    }

    void print_items() {
        size_t i;
        for(i = 0; i < b.ArrayLength(); i++)
        {
            printf("%d\n", b.Array()[i]);
        }
    }
};


int main()
{
    a x;
    x.print_items();
    return 0;
}

ConstFixedSizeArrayFiller и ConstFixedSizeArray могут использоваться повторно.

Первая позволяет проверять границы времени выполнения при инициализации массива (так же, как вектор может), который позже может стать const после этой инициализации.

Второе позволяет выделить массив в другом объекте, который может находиться в куче или просто в стеке, если это объект. Там нет траты времени, выделяемого из кучи. Он также выполняет проверку константы compile-time в массиве.

b_filler - это небольшой частный класс для предоставления значений инициализации. Размер массива проверяется во время компиляции с аргументами шаблона, поэтому нет возможности выйти за пределы.

Я уверен, что есть более экзотические способы изменить это. Это начальный удар. Я думаю, вы можете в значительной степени компенсировать любой из недостатков компилятора с классами.

Ответ 5

Стандарт ISO С++ не позволяет вам это делать. Если бы это было так, синтаксис, вероятно, был бы следующим:

a::a(void) :
b({2,3})
{
    // other initialization stuff
}

Или что-то в этом роде. Из вашего вопроса это на самом деле похоже на то, что вы хотите, это постоянный класс (aka static), который является массивом. С++ позволяет это сделать. Например:

#include <iostream>

class A 
{
public:
    A();
    static const int a[2];
};

const int A::a[2] = {0, 1};

A::A()
{
}

int main (int argc, char * const argv[]) 
{
    std::cout << "A::a => " << A::a[0] << ", " << A::a[1] << "\n";
    return 0;
}

Выход:

A::a => 0, 1

Теперь, конечно, поскольку это статический член класса, он одинаковый для каждого экземпляра класса A. Если это не то, что вы хотите, т.е. вы хотите, чтобы каждый экземпляр A имел разные значения элементов в массиве a, тогда вы "Сделаем ошибку, пытаясь сделать массив const начальным. Вы должны просто делать это:

#include <iostream>

class A 
{
public:
    A();
    int a[2];
};

A::A()
{
    a[0] = 9; // or some calculation
    a[1] = 10; // or some calculation
}

int main (int argc, char * const argv[]) 
{
    A v;
    std::cout << "v.a => " << v.a[0] << ", " << v.a[1] << "\n";
    return 0;
}

Ответ 7

Где у меня есть постоянный массив, он всегда делался как статический. Если вы можете принять это, этот код должен компилироваться и выполняться.

#include <stdio.h>
#include <stdlib.h>

class a {
        static const int b[2];
public:
        a(void) {
                for(int i = 0; i < 2; i++) {
                        printf("b[%d] = [%d]\n", i, b[i]);
                }
        }
};

const int a::b[2] = { 4, 2 };

int main(int argc, char **argv)
{
        a foo;
        return 0;
}

Ответ 8

Решение без использования кучи с std::vector заключается в использовании boost::array, хотя вы не можете инициализировать элементы массива непосредственно в конструкторе.

#include <boost/array.hpp>

const boost::array<int, 2> aa={ { 2, 3} };

class A {
    const boost::array<int, 2> b;
    A():b(aa){};
};

Ответ 9

Интересно, что в С# у вас есть ключевое слово const, которое переводит в С++ static const, в отличие от readonly, которое может быть установлено только в конструкторах и инициализациях даже неконстантами, ex:

readonly DateTime a = DateTime.Now;

Согласен, если у вас есть предопределенный массив const, вы можете сделать его статическим. В этот момент вы можете использовать этот интересный синтаксис:

//in header file
class a{
    static const int SIZE;
    static const char array[][10];
};
//in cpp file:
const int a::SIZE = 5;
const char array[SIZE][10] = {"hello", "cruel","world","goodbye", "!"};

однако, я не нашел пути вокруг константы '10'. Причина ясна, но ему нужно знать, как выполнить доступ к массиву. Возможной альтернативой является использование #define, но мне не нравится этот метод, а я #undef в конце заголовка, с комментарием, чтобы редактировать его там, где есть CPP, и в случае изменения.

Ответ 10

Как насчет эмуляции массива const через функцию accessor? Он нестатический (по вашему запросу), и он не требует stl или любой другой библиотеки:

class a {
    int privateB[2];
public:
    a(int b0,b1) { privateB[0]=b0; privateB[1]=b1; }
    int b(const int idx) { return privateB[idx]; }
}

Поскольку a:: privateB является приватным, он фактически является постоянным за пределами::, и вы можете получить к нему доступ аналогично массиву, например.

a aobj(2,3);    // initialize "constant array" b[]
n = aobj.b(1);  // read b[1] (write impossible from here)

Если вы хотите использовать пару классов, вы можете дополнительно защитить privateB от функций-членов. Это можно сделать, наследуя a; но я думаю, что предпочитаю пост Джона Харрисона comp.lang.С++, используя класс const.