How to store temporary state, needed for the initializer list, in the constructor (on the stack)?
For example, implementing this constructor …
// configabstraction.h
#include <istream>
class ConfigAbstraction
{
public:
ConfigAbstraction(std::istream& input);
private:
int m_x;
int m_y;
int m_z;
};
… using a stateful helper class like this?
// mysillyparserdontworry.h
#include <json/reader.h> //jsoncpp
class MySillyParserDontWorry
{
public:
MySillyParserDontWorry(std::istream& input) { input >> m_parseTree; }
int intByName(const char* name) const { return m_parseTree[name].asInt(); }
private:
Json::Value m_parseTree;
};
My attempt:
// configabstraction.cpp
ConfigAbstraction::ConfigAbstraction(std::istream& input)
: local_parserState(input) // init local variable first: Not possible!
, m_a(local_parserState.intByName("a"))
, m_b(local_parserState.intByName("b"))
, m_c(local_parserState.intByName("c"))
{
MySillyParserDontWorry local_parserState; // ...because it is local
}
With C++11 you could solve this with delegating constructors:
class ConfigAbstraction
{
public:
ConfigAbstraction(std::istream& input);
private:
ConfigAbstraction(const MySillyParserDontWorry& parser);
int m_a;
int m_b;
int m_c;
};
ConfigAbstraction::ConfigAbstraction(const MySillyParserDontWorry& parser)
: m_a{parser.intByName("a")}
, m_b{parser.intByName("b")}
, m_c{parser.intByName("c")}
{
}
ConfigAbstraction::ConfigAbstraction(std::istream& input)
: ConfigAbstraction{MySillyParserDontWorry{input}}
{
}
An alternative solution to your problem would be packing the three individual ints into a common data structure. This would allow you to initialise an object of that type with a private static helper function. Being able to initialise the object instead of assigning to it later also allows for it to be const (if that is required).
Here is an example with std::tuple. But you could also create your own helper struct or even std::array<int, 3>; the basic idea remains the same: have one member object instead of three.
#include <istream>
#include <tuple>
class MySillyParserDontWorry
{
public:
MySillyParserDontWorry(std::istream& input) { /* ... */ }
int intByName(const char* name) const { return /* ... */ 0; }
};
class ConfigAbstraction
{
public:
ConfigAbstraction(std::istream& input);
private:
static std::tuple<int, int, int> parse(std::istream& input)
{
std::tuple<int, int, int> result;
MySillyParserDontWorry parser(input);
std::get<0>(result) = parser.intByName("a");
std::get<1>(result) = parser.intByName("b");
std::get<2>(result) = parser.intByName("c");
return result;
}
std::tuple<int, int, int> const m;
};
ConfigAbstraction::ConfigAbstraction(std::istream& input)
: m(parse(input))
{
}
Why not simply doing the assignments in the constructor's body then?
ConfigAbstraction::ConfigAbstraction(std::istream& input)
: m_a(0)
, m_b(0)
, m_c(0)
{
MySillyParserDontWorry local_parserState;
m_a = local_parserState.intByName("a");
m_b = local_parserState.intByName("b");
m_c = local_parserState.intByName("c");
}
Is there any specific requirement that hinders you doing that?
What an artificial limitation of C++!
It's not an artificial limitation. How should initialisation of local variables be done outside of their function's scope? It would just lead to a great confusion, where variables are actually initialised (naming conflicts aside).
You can not initialize local variables bedfore members. The reason for this is very simple (so that it is NOT an artificial limitation):
Members have to be initialized (constructed) before constructor body begins, because constructor body might access them - and they need to be initialized for this access. On the other hand, local variables do not exist before code enters constructor body (as for any other function).
Conclusion - initialization of local variables before members is impossible.
Related
i have a class,the class contains a large size of std::array,how to initialize the array??
see class test;
sample:
class context{......}
class value
{
public:
value(context& ctx) : ctx_(ctx){
}
protected:
context& ctx_;
int data_ = 0;
}
class test
{
public:
test() : /*i need to initialize values at here*/ values_{ctx_,.....}
{
}
protected:
context ctx_;
std::array<value_t,10000> values_;
}
in reality,this array maybe only contains 3 or 5 element,not 10000,but someof people definitely gonna give me an answer like below
test() : values_{ctx_,ctx_,ctx_,ctx_,ctx_}
{
}
i don't need a awkward answer like above.
is there a way to initialize std::array with simple code like fold expression???
You can delegate to a constructor that takes a parameter pack then fold over that:
#include <utility>
#include <cstddef>
class test
{
public:
test() : test(std::make_index_sequence<10000>{}) {}
private:
template<std::size_t... I>
test(std::index_sequence<I...>) : values_{{(I, ctx_)...}} {}
protected:
context ctx_;
std::array<value_t, 10000> values_;
};
Though this absolutely killed compile time at any level of optimisation other than -O0 for me (And will probably blow up your compiled code size)
You could also try constructing into uninitialised memory so you don't need to default construct:
#include <array>
#include <cstddef>
#include <new>
#include <memory>
class test
{
public:
test() {
std::byte* p = value_memory_;
for (std::byte* end = std::end(value_memory_); p < end; p += sizeof(value_t)) {
new (p) value_t(ctx_);
}
}
~test() {
value_t* values = get_values();
std::destroy(values, values + 10000);
}
protected:
context ctx_;
value_t* get_values() {
return std::launder(reinterpret_cast<value_t*>(value_memory_));
}
const value_t* get_values() const {
return std::launder(reinterpret_cast<const value_t*>(value_memory_));
}
// These are UB, but work on most compilers, and would generally be nicer
// to work with
value_t(&get_values())[10000] {
return *std::launder(reinterpret_cast<value_t(*)[10000]>(value_memory_));
}
const value_t(&get_values() const)[10000] {
return *std::launder(reinterpret_cast<const value_t(*)[10000]>(value_memory_));
}
private:
alignas(value_t) std::byte value_memory_[sizeof(value_t) * 10000u];
};
Which will have some runtime cost, and you have to lose the std::array (Unless you go for a std::array<std::aligned_storage_t<sizeof(value_t), alignof(value_t)>, 10000>, in which case you have to launder every single element of the array)
The problem is that your array holds elements of a type that does not have a default constructor, so when you declare a std::array holding that type, the array can only be initialized using aggregate initialization so you can explicitly pass in a value to each element's constructor. When the array is a member of a class or struct, that initialization requires use of the class/struct constructor's member initialization list. Exactly what you are trying to avoid.
To get around this, you can use placement-new to explicitly construct each array element individually in a loop:
#include <type_traits>
class context{......}
class value
{
public:
value(context& ctx) : ctx_(ctx){
}
protected:
context& ctx_;
int data_ = 0;
}
class test
{
public:
test()
{
for (auto &v : values_)
new (&v) value(ctx_);
}
~test()
{
for (auto &v : values_) {
// note: needs std::launder in C++17 and later
// std::launder(reinterpret_cast<value*>(&v))->~value();
reinterpret_cast<value*>(&v)->~value();
}
}
protected:
context ctx_;
using storage_type = std::aligned_storage<sizeof(value), alignof(value)>::type;
std::array<storage_type, 10000> values_;
// Access an object in aligned storage
value& operator[](std::size_t pos)
{
// note: needs std::launder in C++17 and later
// return *std::launder(reinterpret_cast<value*>(&values_[pos]));
return *reinterpret_cast<value*>(&values_[pos]);
}
};
You can use fill() method on the array:
https://en.cppreference.com/w/cpp/container/array/fill
I have recently started learning OOP in C++ and I started solving example tasks regarding it. I want to instantiate an object of the class CStudent after having created a default constructor for it. However the compiler cannot compile the code. I would like to ask why is that?
When you write inside your class:
CStudent();
CStudent(string name, string fn);
...you only declare two constructors, one default (taking no-argument) and one taking two strings.
After declaring them, you need to define them, the same way you defined the methods getName or getAverage:
// Outside of the declaration of the class
CStudent::CStudent() { }
// Use member initializer list if you can
CStudent::CStudent(std::string name, string fn) :
name(std::move(name)), fn(std::move(fn)) { }
In C++, you can also define these when declaring them inside the class:
class CStudent {
// ...
public:
CStudent() { }
CStudent(std::string name, string fn) :
name(std::move(name)), fn(std::move(fn)) { }
// ...
};
Since C++11, you can let the compiler generate the default constructor for you:
// Inside the class declaration
CStudent() = default;
This should work, As commented by Holt, You need to define constructor, You have just declared it.
#include <iostream>
#include <string>
#include <list>
using namespace std;
class CStudent {
string name = "Steve";
list<int> scores;
string fn;
public:
CStudent() {};
CStudent(string name, string fn);
string getName();
double getAverage();
void addScore(int);
};
string CStudent::getName() {
return name;
}
double CStudent::getAverage() {
int av = 0;
for (auto x = scores.begin(); x != scores.end(); x++) {
av += *x;
}
return av / scores.size();
}
void CStudent::addScore(int sc) {
scores.push_back(sc);
}
int main()
{
CStudent stud1;
cout<< stud1.getName()<< endl;
return 0;
}
I wonder if there is a workaround is such situation:
class A
{
class
{
public:
void setValue(int val) {i=val;}
private:
int i;
} B = initB(10);
std::function<decltype(B)(int)> initB = [this](int value)
{decltype(B) temp;
temp.setValue(value);
return temp;};
}
//...
A a; //crash
//...
I suppose it is caused by order of initialization. Variable B is initilized by calling an uninitilized std::function instance, hence the crash. By my logic, the workaround would be to initialize std::function first, then initialize member B. But then, such code is not valid:
class A
{
//error: 'B' was not declared in this scope
std::function<decltype(B)(int)> initB = [this](int value)
{decltype(B) temp;
temp.setValue(value);
return temp;};
class
{
public:
void setValue(int val) {i=val;}
private:
int i;
} B = initB(10);
}
I tried to make to make the std::function static, and such code works, but requires non-constexpr/const member, because std::function has non-trivial destructor - which is bad, because that requires source file, which requires creating such file, which requires some efford and destruction of my beautiful header-only class hierarchy! (I mean, I could be lazy and define this variable in the header, but then the multiple definition problem occurs). I know it might be a bad design (i'm just testing things out), but do you have any ideas how the problem can be solved without involving source files?
Although your example is contrived, there are times when I've needed (or its more convenient) to initialize complex objects in a similar way.
But, why use std::function<>? Why not just use a function?
class A
{
class
{
public:
void setValue(int val) { i = val; }
private:
int i;
} B = initB(10);
static decltype(B) initB(int value)
{
decltype(B) temp;
temp.setValue(value);
return temp;
}
};
Although, I wouldn't normally use decltype(B); I would just give the class a name.
I feel like I am somehow subverting your intent, but if you initialize the variables in the constructor, you can make things work.
#include <functional>
class A
{
class
{
public:
void setValue(int val) {i=val;}
private:
int i;
} B;
std::function<decltype(B)(int)> initB;
public:
A() {
initB = [this](int value)
{decltype(B) temp;
temp.setValue(value);
return temp;};
B = initB(10);
}
};
int main() {
A a;
}
A::initB is a value. It's not initialized at the point where you call it, because initialization is done (loosely speaking) in the order you specify member fields. You can verify this by executing the below, which works:
#include <iostream>
#include <functional>
using namespace std;
template<typename T, typename U>
T set(T& tgt, const U& src)
{
new(&tgt) T(src);
return tgt;
}
class A
{
class
{
public:
void setValue(int val) {i=val;}
private:
int i;
} B = set(initB, [this](int value)
{decltype(B) temp;
temp.setValue(value);
return temp;})(10);
std::function<decltype(B)(int)> initB;
};
int main() {
A a;
}
I have a constant value that never changes during run-time, but is impossible to know until run-time.
Is there a way to declare a constant (either as a member of a class or not) without defining it and also assign a computed value once (and only once) it is determined; or am I going to have to resort to a non-const declaration and use coding S & Ps (ALL_CAPS variables names, static declaration if in a class, etc.) to try and keep it from changing?
CLARIFICATION:
Though these are good answers, the real-world situation I have is more complicated:
The program has a main loop that continually runs between processing and rendering; the user can set required options and once they are set they will never change until the program is restart. An "Initialize" function is set up for anything that can be determined before the main loop, but values that are dependent on user interaction must be performed in the middle of the loop during the processing phase. (At the moment, persistent data storage techniques come to mind...)
Something like this?
const int x = calcConstant();
If it's a class member, then use the constructor initialisation list, as in Yuushi's answer.
You can define it in a struct or class and utilize an initialisation list:
#include <iostream>
struct has_const_member
{
const int x;
has_const_member(int x_)
: x(x_)
{ }
};
int main()
{
int foo = 0;
std::cin >> foo;
has_const_member h(foo);
std::cout << h.x << "\n";
return 0;
}
As a static or function-local variable:
const int x = calcConstant();
As a class member:
struct ConstContainer {
ConstContainer(int x) : x(x) {}
const int x;
};
Yes, you can make a private static singleton field with an initialization method and a gettor method. Here's an example of how to do it:
// In foo.h
class Foo
{
public:
// Caller must ensure that initializeGlobalValue
// was already called.
static int getGlobalValue() {
if (!initialized) {
... handle the error ...
}
return global_value;
}
static void initializeGlobalValue(...)
private:
static bool initialized;
static int global_value;
};
// In foo.cpp
bool Foo::initialized = false;
int Foo::global_value;
void Foo::initializeGlobalValue(...) {
if (initialized) {
...handle the error...
}
global_value = ...;
initialized = true;
}
I want to initialize array-of-classes member of a class
const int count = 5;
class In
{
float rate;
public:
In( int index )
{
rate = index / count;
}
};
class Out
{
In ins[count];
public:
Out() : /* ??? */
{
}
};
Can I obtain index of instance of In in initialization ins if I will rely on initialization list of Out::Out()
You can't do that. You need to define a default constructor for In.
Or better yet, use a std::vector.
EDIT: Might be supported in C++11, not sure though.