Non-const used in a constexpr : what does the standard say? - c++

What does the C++11 iso standard say about such an expression :
class MyClass
{
public:
constexpr int test()
{
return _x;
}
protected:
int _x;
};
_x is a non-const used in a constexpr : will it produce an error, or will the constexpr be simply ignored (as when we pass a non-const parameter) ?

It's perfectly fine, though somewhat useless:
constexpr int n = MyClass().test();
Since MyClass is an aggregate, value-initializing it like that will value-initialize all members, so this is just zero. But with some polish this can be made truly useful:
class MyClass
{
public:
constexpr MyClass() : _x(5) { }
constexpr int test() { return _x; }
// ...
};
constexpr int n = MyClass().test(); // 5

If the expression does not resolve to a constant expression, then it cannot be used as such. But it can still be used:
#include <array>
constexpr int add(int a, int b)
{
return a+b;
}
int main()
{
std::array<int, add(5,6)> a1; // OK
int i=1,
int j=10;
int k = add(i,j); // OK
std::array<int, add(i,j)> a2; // Error!
}

Related

Defining a static member

Consider the following code:
#include <iostream >
using namespace std;
class A
{
private:
int x;
public:
A(int _x) { x = _x; }
int get() { return x; }
};
class B
{
static A a;
public:
static int get()
{ return a.get(); }
};
A B::a(0);
int main(void)
{
B b;
cout << b.get();
return 0;
}
My book says:
If we do not use the line of code A B::a(0),there is a compiler error because static member a is not defined in B. To fix the error, we need to explicitly define a.
However, I thought of initializing object a as static A a(0); but it gives me a compiler error. Can someone explain why I can't initialize object a in the manner I described, and why it is necessary to initialize it as they had given it in book.
If you want to define a inline, you need to inline it, which is possible from C++17:
class B {
inline static A a{0}; // or inline static A a = 0;
public:
static int get() { return a.get(); }
};
Demo

Error when initialazing struct member array

I have this struct:
template<int N>
struct XYarr
{
double x[N],y[N];
int size() {return N;}
};
When I try to intialize it (and return it by a function)
return XYarr<size>{x2, y2}; //x2 and y2 are arrays double[size]
I get the following error: cannot initialize an array element of type 'double' with an lvalue of type 'double [200]'. What is the problem?
Raw arrays don't have value semantics. Thus they can't be initialized or assigned using the = operator and need to be copied explicitly. For example, using a constructor like this:
#include <algorithm>
template<int N>
struct XYarr
{
double x[N], y[N];
XYarr(const double(&x2)[N], const double(&y2)[N]) {
std::copy(std::begin(x2), std::end(x2), x);
std::copy(std::begin(y2), std::end(y2), y);
}
int size() const { return N; }
};
int main() {
double x2[4]{}, y2[4]{};
XYarr<4> test{ x2, y2 };
}
Compiler can't use member aggregate initialization because C-arrays can't be copied.
To overcome this, provide a constructor for your struct:
template<int N>
struct XYarr
{
XYarr() = default; // default constructor if needed
XYarr(const double (&x_)[N], const double (&y_)[N]) {
memcpy(&x,x_,sizeof (double)*N);
memcpy(&y,y_,sizeof (double)*N);
}
double x[N],y[N];
int size() {return N;}
};
int main(int argc, char * argv[]) {
XYarr<100> a; // default constructor
double x2[100], y2[100];
auto n = XYarr<100>{x2, y2}; // init constructor
return 0;
}
There is an std::array version for comparison. std::array can be used with member aggregate initialization - so no boilerplate code, everything is simple.
template<int N>
struct XYarr_stl
{
std::array<double,N> x,y;
int size() {return N;}
};
int main(int argc, char * argv[]) {
std::array<double,100> x2_stl, y2_stl;
auto n_stl = XYarr_stl<100>{x2_stl, y2_stl};
// this will give compilation error, because array sizes will be checked at compile time
//std::array<double,200> x2_stl2, y2_stl2;
//auto n_stl2 = XYarr_stl2<100>{x2_stl2, y2_stl2};
return 0;
}

static constexpr function different than global?

I cannot understand why static constexpr behaves differently then global constexpr. What am I doing wrong? Compiler error is not particularly helpful:
prog.cpp:20:17: error: ‘static constexpr int Bar::foo(const char*)’ called in a constant expression
B = Bar::foo(s)
^
prog.cpp:20:17: error: enumerator value for ‘B’ is not an integer constant
code is as follows:
#include <iostream>
constexpr int foo(const char* v)
{
return v[0];
}
struct Bar
{
static constexpr int foo(const char* v)
{
return v[0];
}
static constexpr const char* s = "abc";
enum
{
A = ::foo(s),
B = Bar::foo(s)
};
};
int main()
{
int a[Bar::A];
a[0] = Bar::A;
}
This is because the enum is part of the class declaration, whereas the definitions of the functions inside the class are logically deferred until after the class declaration. This means that as far as the enum is concerned, it can't see the definition of B::foo, and therefore it can't use a call to it as a constant expression. You can see this by putting the enum after the class:
#include <iostream>
constexpr int foo(const char* v)
{
return v[0];
}
struct Bar
{
static constexpr int foo(const char* v)
{
return v[0];
}
static constexpr const char* s = "abc";
};
enum Enum
{
A = ::foo(Bar::s),
B = Bar::foo(Bar::s)
};
which gives no error.

How to make brace initialization and default values work together?

The following code works
class A
{
public:
int i;
float f;
};
int main()
{
A a{ 1, 0.1 };
return 0;
}
However, if I add default values for A's members, it doesn't work
class A
{
public:
int i = 0;
float f = 3.14;
};
How to make both work together?
You have to define a default and a custom constructor like the example below:
class A
{
public:
A() {}
A(int const _i, float const _f) : i(_i), f(_f) {}
int i = 0;
float f = 3.14;
};
LIVE DEMO
However as already mentioned by #Kerek SB, #T.C. in the comments this will be fixed in C++14 and your code will work as is.

Initializing constant array of fixed size inside class

Consider the following class:
class A {
const int arr[2];
public:
A() { }
};
Is it possible to initialize arr from the constructor initializer list or in any other way than on the line where it is declared (i.e. const int arr[2] = {1,2};)?
Note that I'm interested in methods that work with C++98!
By wrapping them in a struct, e.g.:
class A
{
struct Data
{
int arr[2];
};
Data const arr;
public:
A() : arr( someOtherStruct ) {}
};
This does mean that to access the data, you'd have to write arr.arr.
It's possible to avoid that by inheriting from the struct:
struct PrivateDataForA
{
int arr[2];
};
class A : private PrivateDataForA
{
public:
A() : PrivateDataForA( someOtherStruct ) {}
};
This does make the name of the struct visible outside of the class
(which might be an advantage—client code could pass you one as an
argument).
If you don't have an instance of the struct handy, say because you want
to fill it with values calculated from arguments to the constructor, you
can use a static member function:
class A : private PrivateDataForA
{
static PrivateDataForA createInitializer( int a, int b );
public:
A( int a, int b ) : PrivateDataForA( createInitializer( a, b ) )
{
}
};
For the OP’s concrete example:
#include <iostream>
#include <stddef.h>
typedef ptrdiff_t Size;
typedef Size Index;
template< class Element, Size n >
struct Array{ Element elem[n]; };
class A {
Array<int, 2> const arr_; // const int arr[2];
A& operator=( A const& ); // No such.
static Array<int, 2> const& oneAndTwo()
{
static Array<int, 2> const a = {1, 2};
return a;
}
public:
A(): arr_( oneAndTwo() ) {}
int at( Index i ) const { return arr_.elem[i]; }
};
int main()
{
using namespace std;
A o;
for( int i = 0; i < 2; ++i )
{
cout << o.at( i ) << endl;
}
}
Initializing array elements to non-zero values requires C++11 support.
In C++03, it's only possible to value-initialize your array, resulting in each element's value being 0:
class A {
const int arr[2];
public:
A() : arr() { }
};
For the relevant C++03 standardese, see this question and answer:
How can i use member initialization list to initialize it?
(I'm going to assume that by C++98 you mean not C++11, i.e. that C++03 is acceptable. If this assumption is wrong, please say so.)
No. It's not.