Я работал над заданием, которое мы получили от нашего профессора, где мне нужно поработать над классом вариационных шаблонов. Проблема в том, что я не могу изменить членов класса в рекурсивном конструкторе. Я не могу понять, почему это так, как только он перейдет к следующему вызову конструктора, он отменит мои изменения в переменной.
Я уже часами гуглял, но не могу найти решающего ответа.
Ответ 2
Как объясняет VTT, вызов Counter()
внутри тела конструктора создает новый объект Counter()
.
Вы можете рекурсивно вызывать конструкторы, но вы должны сделать это в списке инициализации: ищите "делегирующие конструкторы" для получения дополнительной информации.
Я бы также посоветовал вам не инициализировать (и не модифицировать) объект-член внутри тела конструкторов.
Если ваша цель инициализировать count
с количеством аргументов и tmp
со значением последнего аргумента, я предлагаю следующее (основанное на "распределении тегов)" решение
class Counter
{
private:
struct tag
{ };
int count = 0;
int tmp;
Counter (tag tg, std::size_t c0, int t) : count(c0), tmp{t}
{ std::cout << "end: " << tmp << ", " <<count << "\n"; }
template <typename... Rest>
Counter (tag t0, std::size_t c0, int t, Rest... rest)
: Counter{t0, c0, rest...}
{ std::cout << "recursion: " << tmp << ", " << count << "\n"; }
public:
template <typename... Rest>
Counter (Rest... rest) : Counter{tag{}, sizeof...(Rest), rest...}
{ std::cout << "start: " << tmp << ", " << count << "\n"; }
};
Вы также можете избежать отправки тегов и рекурсии конструктора, делегируя рекурсию об rest...
методу (возможно, static
а также constexpr
, если хотите), используемому для инициализации tmp
class Counter
{
private:
int count = 0;
int tmp;
static int getLastInt (int i)
{ return i; }
template <typename ... Rest>
static int getLastInt (int, Rest ... rs)
{ return getLastInt(rs...); }
public:
template <typename... Rest>
Counter (Rest... rest)
: count(sizeof...(Rest)), tmp{getLastInt(rest...)}
{ std::cout << tmp << ", " << count << "\n"; }
};
Не по теме: если быть точным, ваш класс Counter
не является "классом вариационных шаблонов".
Это обычный (не шаблонный) класс с одним (двумя, в моем первом решении) конструктором (-ами) шаблонов с переменными числами.
-- РЕДАКТИРОВАТЬ --
ОП спрашивает
Что, если мне нужно получить счетчик как статическую переменную const и массив int с длиной счетчика во время компиляции и в качестве членов класса? (Массив будет заполнен всеми аргументами конструктора) Это в пределах возможностей C++?
Статический const (может также constexpr
) имеет смысл только в том случае, если счетчик является общим значением для всех экземпляров класса.
На данный момент не имеет смысла, потому что ваш Counter
принимает списки инициализации разной длины.
Но предположим, что номер аргумента конструктора является параметром шаблона (скажем, N
)... в этом случае count
просто N
и может быть static constexpr
. Вы можете определить std::array<int, N>
для значений (также int[N]
но я советую по возможности избегать использования массивов в стиле C и использовать вместо них std::array
) и, делая Конструктор constexpr
, вы можете навязать время компиляции инициализации.
Ниже приведен полный пример компиляции C++ 14 (используются std::make_index_sequence
и std::index_sequence
которые, к сожалению, доступны только начиная с C++ 14).
Заметьте, что я определил переменную f8
в main()
как constexpr
: только так вы можете навязать (притворяясь, что нет правила "как есть"), что f8
инициализируется во время компиляции
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t>
using getType = T;
template <std::size_t N, typename = std::make_index_sequence<N>>
struct foo;
template <std::size_t N, std::size_t ... Is>
struct foo<N, std::index_sequence<Is...>>
{
static_assert( sizeof...(Is), "!" );
static constexpr auto count = N;
const std::array<int, N> arr;
constexpr foo (getType<int, Is> ... is) : arr {{ is ... }}
{ }
};
int main ()
{
constexpr foo<8u> f8 { 2, 3, 5, 7, 11, 13, 17, 19 };
for ( auto const & i : f8.arr )
std::cout << i << ' ';
std::cout << std::endl;
}
Если вы можете использовать компилятор с поддержкой C++ 17, вы также можете использовать руководство по foo
для foo
template <typename ... Args>
foo(Args...) -> foo<sizeof...(Args)>;
поэтому нет необходимости объяснять аргумент шаблона, определяющий f8
// .......VVV no more "<8u>"
constexpr foo f3{ 2, 3, 5, 7, 11, 13, 17, 19 };
потому что выводится из номера аргумента конструктора.