When non-const members can be used in constexpr member functions? - c++

I encountered a situation I don't understand. Would somebody be so nice to explain why first code compiles correctly while second gives an error:
error: the value of 'TestClass::z' is not usable in a constant expression
static constexpr int sum() {return x+y+z;}
----------------------------------------------------^
note: 'int TestClass::z' is not const
static int z;"
Working code:
#include <iostream>
using namespace std;
class TestClass
{
public:
constexpr int sum() {return x+y+z;}
private:
static constexpr int x = 2;
static const int y = 3;
int z = 5;
};
int main()
{
TestClass tc;
cout << tc.sum() << endl;
return 0;
}
But when I try to make TestClass::sum() static I get aforementioned error:
#include <iostream>
using namespace std;
class TestClass
{
public:
static constexpr int sum() {return x+y+z;}
private:
static constexpr int x = 2;
static const int y = 3;
static int z;
};
int TestClass::z = 5;
int main()
{
TestClass tc;
cout << tc.sum() << endl;
return 0;
}
P.S. I'm using mingw32-g++ 4.8.1

In the first case, the result depends only on the function's arguments, including the implicit this used to access z. This doesn't disqualify it from being constexpr - if all the arguments are constant expressions, then so is the result.
In your example, it isn't a constant expression (since tc isn't), but that doesn't matter since it's not being used in a context that requires one. Here's an example showing its use in a constant expression:
constexpr TestClass tc;
array<int, tc.sum()> a;
cout << a.size() << endl;
In the second case, the result also depends on a static variable, whose value could change during the program. This does disqualify it - even if all the arguments are constant expressions, z isn't, and so the result of a function call can never be a constant expression.

Related

unable to include multiple expressions in initialization of a variable c++

It is stated on the site cppreference.com, something like that
For each declarator, the initializer may be one of the following:
( expression-list ) (1)
= expression (2)
{ initializer-list } (3)
comma-separated list of arbitrary expressions and braced-init-lists in parentheses
But in my code
int main(){
int a,b=5,c(a,b);
return 0;
}
when I try to compile, the following error occurs
...error: expression list treated as compound expression in initializer [-fpermissive]
My question is, if list of multiple expressions is allowed in such style of initialization, then why the compiler is not accepting it with variable c?
What am I missing?
All right, let's look at this:
int main(){
int a,b=5,c(a,b);
return 0;
}
What do you expect c(a,b) to actually do?
Let's simplify this just slightly:
int main(){
int a,b=5;
int c(a,b);
return 0;
}
This will generate the same syntax error, but it now stands alone. So...
Your code would work if there were a constructor for int that took two ints as parameters. This would also compile:
int c(int a, int b);
But in that case, you're actually defining a function.
Also, this works:
int main() {
int a = 5;
int b = 10;
int c(b);
std::cout << "C == " << c << std::endl;
}
That works because an int can be initialized from a single int. But you're getting an error because you can't initialize an int from two other ints.
This works:
#include <iostream>
class MyClass {
public:
MyClass(int a, int b): value(a + b) {}
int value;
};
int main() {
int a = 5;
int b = 10;
MyClass c(a, b);
std::cout << "C == " << c.value << std::endl;
}
And maybe that's what the article you read was trying to tell you. Note: cpppreference is NOT a good site for learning C++. Get a good book.

Unable to instantiate generic function in a loop

I have some generic function, which looks similar to
template<int P>
struct foo {
static const int value = ...;
};
Now, I want to call this generic function in a loop. Like so:
for (int i = 0; i < 100; ++i)
{
auto bar = foo<i>::value;
}
But I get a lot of error messages. Like
the value of 'i' is not used in a constant expression
int i is not constant
I tried to fix it with:
foo<(const int)i>::value;
But to no avail. So, what is wrong with that and how can I make it work?
You cannot do it this way.
for (int i = 0; i < 100; ++i)
{
auto bar = foo<i>::value;
}
i needs to me a constant expression, so that the compiler can generate the code for it when it compiles your program.
Here's an exhaustive explanation of what constant expressions are:
http://en.cppreference.com/w/cpp/language/constant_expression
Here's a segment from the site:
int n = 1;
std::array<int, n> a1; // error: n is not a constant expression
const int cn = 2;
std::array<int, cn> a2; // OK: cn is a constant expression
So to make it happen compile time, you need to make your loop into a template recursion using variadic templates.
Maybe you can understand what you need to do better if you read this example:
// Example program
#include <iostream>
#include <string>
template<int P>
struct foo
{
static const int value = P;
};
template <int TIndex>
int call()
{
return foo<TIndex>::value;
}
template <int TIndex, int TIndex2, int ...Rest>
int call ()
{
return call<TIndex>() + call<TIndex2, Rest...>();
}
int main()
{
std::cout << "Test: " << call<1, 2>() << "\n"; // prints "Test: 3"
}
Nicky C posted a link to another question. It has a good answer and I don't feel right in copying it here. Take a look at my working example above and then look at the answer here:
https://stackoverflow.com/a/11081785/493298
You should be able to make it work. It's a bit of a syntax hell, but you can manage it, I'm sure.

Mutable Variable in Class -- Issue

#include <iostream>
using std::cout;
class Test
{
public:
int x;
mutable int y;
Test()
{
x = 4; y = 10;
}
static void disp(int);
};
void Test::disp(int a)
{
y=a;
cout<<y;
}
int main()
{
const Test t1;
Test::disp(30);
t1.y = 20;
cout << t1.y;
return 0;
}
I am getting error with in the constructor:
void Test::disp(int a)
{
y=a;
cout<<y;
}
I don't understand why this is not working because y is mutable and its already updated successfully within constructor Test() but when its coming to disp(). its shows error..
I have also checked with some other examples also . So I came to know you can update a mutable variable once only. If you try to update it more then one time it shows an error. Can anyone explain why this happening or reason behind it?
Your problem doesn't have anything to do with mutable. You are trying to modify a non-static class member from a static method, which is not allowed. In your example it makes little sense to have the Test::disp method static in the first place.
You also seem to have misunderstood the meaning of mutable. It doesn't make members of a const object non-read-only. It makes it possible for const methods to write to members. Your code updated to show what mutable does:
#include <iostream>
using std::cout;
class Test
{
public:
int x;
mutable int y;
Test()
{
x = 4; y = 10;
}
void disp(int) const; // Notice the const
};
void Test::disp(int a) const
{
y=a; // ::disp can write to y because y is mutable
cout<<y;
}
int main()
{
Test t1;
t1.disp(30);
t1.y = 20;
cout << t1.y;
return 0;
}
And yes, there is no limit to the number of times a mutable variable can be written, just to be clear.

How to initialize the reference member variable of a class?

Consider the following code C++:
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test (int &x) { t = x; }
int getT() { return t; }
};
int main()
{
int x = 20;
Test t1(x);
cout << t1.getT() << " ";
x = 30;
cout << t1.getT() << endl;
return 0;
}
It is showing the following error while using gcc compiler
est.cpp: In constructor ‘Test::Test(int&)’:
est.cpp:8:5: error: uninitialized reference member ‘Test::t’ [-fpermissive]
Why doesn't the compiler directly call the Constructor?
That is because references can only be initialized in the initializer list. Use
Test (int &x) : t(x) {}
To explain: The reference can only be set once, the place where this happens is the initializer list. After that is done, you can not set the reference, but only assign values to the referenced instance. Your code means, you tried to assign something to a referenced instance but the reference was never initialized, hence it's not referencing any instance of int and you get the error.
My compiler emits this error:
error C2758: 'Test::t' : must be initialized in constructor base/member initializer list
And that's exactly what you must do. References must be initialized in the initializer list:
#include<iostream>
using namespace std;
class Test {
int &t;
public:
Test (int &x) : t(x) { } // <-- initializer list used, empty body now
int getT() { return t; }
};
int main()
{
int x = 20;
Test t1(x);
cout << t1.getT() << " ";
x = 30;
cout << t1.getT() << endl;
return 0;
}
Explanation:
If the reference is not in the initiliazer list, it's next to impossible for the compiler to detect if the reference is initialized. References must be initialized. Imagine this scenario:
Test (int &x, bool b)
{
if( b ) t = x;
}
Now it would be up to the caller of the constructor to decide if valid code was generated. That cannot be. The compiler must make sure the reference is initialized at compile time.

Static class member

What's wrong with the code below? Latest version of g++ and clang both give error. I am sure I am missing something basic here.
#include <iostream>
struct Z
{
static const int mysize = 10;
};
Z f2();
int main()
{
std::cout << f2()::mysize << std::endl;
}
The motivation here is to be able to find out the size of an array using templates using code such as below. I know there are many ways, but just stumbled upon this idea.
template<int N> struct S
{
enum { mysize = N };
};
template<class T, int N> S<N> f(T (&)[N]);
int main()
{
char buf[10];
std::cout << f(buf)::mysize << std::endl;
}
f2() returns a value, not a type. You'd need to use the . operator on the return value instead of ::
The :: operator requires a type to be named on the lefthand side, while . allows for a value to be named. Your expression f2() does not name a type so it cannot be used in conjunction with ::.
As a side note, with a little more detail in the question we might be able to solve your real problem.
Your program contains two mistakes:
You are using the :: operator to access the member of an object. Use operator . ("dot") instead;
You declare function f2() and invoke it without defining it (this will give you a linker error).
Also, since static member variables are shared among all instances of a class (Z in this case), you do not need an object to access it;
Here is how you could fix your program:
#include <iostream>
struct Z
{
static const int mysize = 10;
};
Z f2() { return Z(); }
int main()
{
// Don't need an object to access a static variable...
std::cout << Z::mysize << std::endl;
// ...but if you really want to, do it this way...
std::cout << f2().mysize << std::endl;
}
Why don't you use this way to find out the size of array by templates:
#include <iostream>
template<int N> struct S
{
enum { mysize = N };
};
template<class T, int N> int f1(T (&)[N])
{
return N;
}
int main()
{
char buf[10];
std::cout << f1(buf) << std::endl;
}
And this one is closer to your variant:
template<class T, int N> S<N> f(T (&)[N])
{
S<N> foo;
return foo;
}
int main()
{
char buf[10];
std::cout << f(buf).mysize << std::endl;
}
Anyway, you will need to return an object from f and access it's member by ., not by ::.
But it's more probable that second variant will be slower, because first variant is fully compile-time, but in the second variant compiler may miss the optimization and don't optimize out the run-time creation of foo.
I think you need to add const int Z::mysize; after class declaration.