Как инициализировать член const, требующий выполнения вычислений?
Я понимаю, что для класса
class A {
const int myint;
public:
A (const int yourint);
A (const std::string yourstring);
};
Я мог бы инициализировать myint
в списке инициализаторов следующим образом:
A::A (const int yourint) : myint (yourint) {};
Однако каков правильный способ инициализации myint
из второго конструктора, если данные, необходимые для его вычисления, скажутся из строки и могут быть задействованы вычисления?
Ответы
Ответ 1
Использовать вызов функции внутри делегационного (если это необходимо, не обязательно) списка инициализации члена конструктора:
A::A(std::string const& yourstring) : A(compute_myint(yourstring)) {};
Pass std::string
на const&
, а не только const
, пока вы на нем.
compute_myint
может быть нечленом, статическим членом, возможно, недоступным извне класса, в зависимости от того, что имеет наибольший смысл.
Ответ 2
Здесь вы хотели бы использовать делегирующие конструкторы, если можете, или можете вычислить в ctor. См. Мой второй пример для второго варианта. Пример для вашего класса:
Вариант 1: Делегирующие конструкторы: С++ 11 вперед
class A {
const int myint;
static int parse_int(const std::string& string) {/*...*/}
public:
A (const int yourint) : myint{yourint};
A (const std::string yourstring) : A{parse_int(yourstring)};
}
Кстати, поскольку parse_int
вычисляет только целые числа, тогда это может быть static
, что означает, что для него не требуется использовать экземпляр класса. Разумеется, нет требования, так как функция также может быть членом (не static
), хотя static
более безопасна, так как она почти всегда гарантирует конструкцию объекта.
Вариант 2: вычисление конструктора, без делегирования
Этот метод может использоваться в любой версии С++.
class A {
const int myint;
static int parse_int(const std::string& string) {/*...*/}
public:
A (const int yourint) : myint(yourint);
A (const std::string yourstring) : my_int(parse_int(yourstring));
}
Ответ 3
Просто используйте функцию-член.
Имейте в виду, что он более безопасен (т.е. меньше подвержен ошибкам) использовать функцию-член static
для таких вещей, как нестатический, потому что класс еще не полностью инициализирован, когда вызывается функция.
class A {
const int myint;
public:
A(const int yourint) : myint(yourint) {}
A(const std::string yourstring) : myint(compute(yourstring)) {}
static int compute(std::string s) { return (int)s.length(); }
};
Ответ 4
Меня это раздражало уже несколько раз, поэтому я разработал небольшую утилиту для ее решения в общем случае. Полный код выглядит следующим образом:
namespace initBlock_detail {
struct tag { };
template <class F>
decltype(auto) operator + (tag, F &&f) {
return std::forward<F>(f)();
}
}
#define initBlock \
initBlock_detail::tag{} + [&]() -> decltype(auto)
И используется следующим образом:
int const i = initBlock {
// Any complex calculation
// and then return the value
return foo;
};
Смотрите вживую на Coliru
Структура похожа на реализацию Andrei Alexandrescu ScopeGuard, которая использует перегрузку оператора infix и лямбда для достижения этого легкого синтаксиса. i
может быть выведен, может быть ссылкой и т.д. Другие полезные функции включают возможность размещения объявлений using namespace
внутри блока init. Можно использовать любой подвижный и/или копируемый тип.