Why does the following code compile with a circular const variable referencing itself?
#include <iostream>
extern int main(int argc, char* argv[])
{
const int foo = foo * 60;
std::cout << foo << std::endl;
return 0;
}
I'm compiling on a Solaris 5.10 x86 host with the SUNWspro compiler:
/opt/SUNWspro/bin/CC test.cpp
For completeness, this is what it prints:
$ ./a.out
-519270512
In C++, variables are in scope and can be used as part of their own initializers. For example, the following code is also legal:
int x = x;
Doing this results in undefined behavior, since you're referring to the value of x before it has been initialized. However, it's perfectly legal code. I think that the rationale behind this is that you might in some cases want an object to refer to itself during its construction. For example, you could conceivably do something like this:
MyObject x(137, &x); // Pass a pointer to x into its own constructor
I'm honestly not sure why you'd ever want to do this, though.
Hope this helps!
Related
This question states that main can be implementation defined with some restrictions.
So, I wrote the following C++ code to try out the following signature of main:
main.h
class MyClass {
private:
int i;
public:
MyClass();
inline int geti() {
return i;
}
inline void seti(int i) {
this->i = i;
}
~MyClass();
};
MyClass::MyClass() {
this->i = 2;
}
MyClass::~MyClass() {
}
main.c++
#include <iostream>
#include "main.h"
int main(MyClass myClass) {
std::cout << myClass.geti() << std::endl;
return 0;
}
Which Gives the following results:
The command g++ -o main main.c++ -O3 compiles successfully with warnings:
main.c++:5:5: warning: first argument of ‘int main(MyClass)’ should be ‘int’ [-Wmain]
5 | int main(MyClass myClass) {
| ^~~~
main.c++:5:5: warning: ‘int main(MyClass)’ takes only zero or two arguments [-Wmain]
The command clang++ -o main main.c++ -std=c++14 gives the error:
main.c++:5:5: error: first parameter of 'main' (argument count) must be of type 'int'
int main(MyClass myClass) {
^
1 error generated.
the main file generated by g++ gives SIGSEGV (why though?)
So, if main can be implementation defined, why does clang give an error while g++ generated file give SIGSEGV?
I also went further and created a different code so that I will be able to pass a MyClass object to main.c++ as follows:
#include <iostream>
#include "main.h"
#include <unistd.h>
int main() {
MyClass myClass;
execve("./main",myClass,NULL);
return 0;
}
However, as execve takes the second parameter to be a char* const *, it does not compile. How do I pass the myClass object to the main file generated by g++?
The command g++ -o main main.c++ -O3 compiles successfully with warnings
This is not successful compilation. You should always use -Werror. If you fail to do so and then decide to ignore the warning and proceed with running the program, it's your own responsibility. You better know full well what you are doing. See this for more information.
the main file generated by g++ gives SIGSEGV (why though?)
The compiler has warned you. It is in your best interest to listen to it. If things go boom, chances are, that's because you have ignored warnings.
why does clang give an error while g++ generated file give SIGSEGV?
The program is not a valid C++ program. There is no meaningful difference between a warning and an error.
How do I pass the myClass object to the main file generated by g++?
You cannot. main must have a form equivalent to one of these two:
int main()
int main(int argc, char* argv[])
(Optional reading in italics) Other forms of main are implementation-defined. This means your implementation needs to support them in a documented way. Unless you have read documentation for your implementation and found that it supports the form of main you want, there's no way to do that.
Other than having an implementation-defined main, the only way a program can get hold of an object of a class type is by constructing that object.
You are close. You have identified your primary issue attempting to pass as a parameter to main() -- that won't work. The declaration for main() is defined by the standard and you are limited to passing string values (nul-terminated character arrays... C-Strings) in as arguments.
In your case you need to create an instance of your class within main(), e.g.
#include <iostream>
#include "main.h"
int main() {
MyClass myClass;
std::cout << myClass.geti() << std::endl;
return 0;
}
Your main.h header has a variable shadowing problem where at line 10:
inline void seti(int i) {
int i shadows a prior declaration at line 3, e.g. int i; (though the consequence would be unlikely to matter). Just replace the variable name in the second declaration with j (or whatever you like). Your code will compile without warning, e.g.
class MyClass {
private:
int i;
public:
MyClass();
inline int geti() {
return i;
}
inline void seti(int j) {
this->i = j;
}
~MyClass();
};
MyClass::MyClass() {
this->i = 2;
}
MyClass::~MyClass() {
}
Example Use/Output
$ ./bin/main
2
You can also call your seti() function to update the private variable in your class, e.g.
myClass.seti(5);
std::cout << myClass.geti() << std::endl;
Which would now output 5.
Let me know if you have further questions.
I have a sample code as shown below:
class myclass
{
public:
static int j;
myclass(){};
~myclass(){};
};
int main(int argc, char** argv) {
myclass obj;
return EXIT_SUCCESS;
}
Now I have declared a static int inside myclass and although I have not defined it, the compiler does not give me any errors until I began using the static variable. Why is that?
Because these are linker errors, not compiler errors. Linker errors never arise until you use an undefined symbol.
"the compiler does not give me any errors until I began using the static variable. Why is that?"
Because it didn't need to be linked with your code until that point (when you start to use it). Unused code is ignored / stripped off by the linker.
// SomeCls.h
class SomeCls
{
static const int PERIOD_ALARM_NORMAL = 5;
static const int PERIOD_ALARM_THRESH = 1;
void method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
} obj;
It is going to build ok. Now take out the method() implementation and place it in a cpp file:
//SomeCls.cpp
#include "SomeCls.h"
void SomeCls::method()
{
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
Why does mr. linker say
undefined reference to SomeCls::PERIOD_ALARM_NORMAL' undefined
reference toSomeCls::PERIOD_ALARM_THRESH'
?
Thanks
EDIT:
It seems to me that that inside .h, the ternary operator takes static const ints it as rvalues but ... outside the decalrative .h, it regards them as lvalue and needs definition.
This is what I have managed to understand from the answers below. Kudos to Bada compiler (some eabi linux thinggie)
If the compiler can't see all the static class constants' values, then you have to provide definitions for them so that they'll actually be stored somewhere. Add the following to your cpp file:
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
This is a GCC limitation, but it's completely standard comforming. Technically a static const int is still an lvalue. You've provided the value inline so compiler will almost always use it as an rvalue. There is one exception. The abstract instructions emitted by the compiler for ternary operators queries the address of lvalues. Hence the error you're seeing.
You can work around this by using enum instead. Or if you're using a new version of GCC constexpr was added to standard to fix this exact problem (named and typed rvalues).
Alternatively you can provide the linker with a definition for the constants. E.g. in your classes cpp file add the a line like
// I wish I had constexpr
const int SomeCls::PERIOD_ALARM_NORMAL;
const int SomeCls::PERIOD_ALARM_THRESH;
As a side note: I was a staunch proponent of static const for class scope constants. Then I found out that MSVC doesn't allow for static const float with the value inline. So the only values you can portably put in a static const are integers, in which case enums provide all the same features plus the guarantee that they'll never silently convert to an lvalue.
If, for whatever reason, you compiler simply refuses to link the code (like GCC 4.4.5 does), here's a simple fix: Replace the static const ints with an enum.
// someclass.h
// include guards, blabla
class SomeClass
{
enum AlarmPeriod{
PERIOD_ALARM_NORMAL = 5,
PERIOD_ALARM_THRESH = 1
};
public:
void method();
};
// someclass.cpp
#include "someclass.h"
void SomeClass::method(){
bool b = true;
const int d = b ? PERIOD_ALARM_THRESH : PERIOD_ALARM_NORMAL;
}
// main.cpp
#include "someclass.h"
int main(){
someclass sc;
sc.method();
}
This links cleanly with GCC 4.4.5, which wouldn't link the former version, even though both are technically the same.
Note that you cannot, amongst other things, take the address of PERIOD_ALARM_NORMAL and PERIOD_ALARM_TRESH anymore, because both names are just aliases for their respective values.
I am baffled by the linker error when using the following code:
// static_const.cpp -- complete code
#include <vector>
struct Elem {
static const int value = 0;
};
int main(int argc, char *argv[]) {
std::vector<Elem> v(1);
std::vector<Elem>::iterator it;
it = v.begin();
return it->value;
}
However, this fails when linking -- somehow it needs to have a symbol for the static const "value."
$ g++ static_const.cpp
/tmp/ccZTyfe7.o: In function `main':
static_const.cpp:(.text+0x8e): undefined reference to `Elem::value'
collect2: ld returned 1 exit status
BTW, this compiles fine with -O1 or better; but it still fails for more complicated cases. I am using gcc version 4.4.4 20100726 (Red Hat 4.4.4-13).
Any ideas what might be wrong with my code?
If you want to initialize it inside the struct, you can do it too:
struct Elem {
static const int value = 0;
};
const int Elem::value;
Try writing it as
struct Elem {
static const int value;
};
const int Elem::value = 0;
etc
.
static class members are generally supposed to be defined outside the class (declared inside, defined outside) in one compilation unit.
I don't remember how that interacts with inline initialization of const static integral members.
Also see this post: essentially, the problem is that somehow compiler ends up expanding your code into taking the address of Elem::value.
Why not just do this?
return Elem::value;
But the answer is that you are assigning a value in the declaration. This is supposed to work for basic types such as int, and is only required for complex types (i.e. classes, such as if you had a string instead of int). What I have found in practice is that this is hit or miss depending on what version of what compiler you are using. And, as you found out, which optimization level.
In most of the compilers defining in-class static const <integral type> works fine. But some compilers like Android NDK, such in-class definitions results in linker errors. For such case, we may use the typed enums:
struct X
{
enum : int64_t { VALUE = 100; }; // == static const int64_t VALUE = 100;
};
The following code doesn't compile for obvious reasons, namely that Foo is trying to access a private member of Bar. However if you uncomment/comment the lines marked, making Foo a template, it does compile and outputs 42. What am I missing here? Why does this work? Seems to me it shouldn't.
Thanks for your help.
#include <iostream>
class Bar {
private:
static const int x = 42;
};
//template <int> // uncomment me
struct Foo {
static const int i = Bar::x;
};
int main(int argc, char* argv[]) {
std::cout << Foo::i << std::endl; // comment me
//std::cout << Foo<0>::i << std::endl; // uncomment me
}
If you are seeing this behavior, it is a compiler bug.
Both Comeau Online and Visual C++ 2010 reject the code as invalid because Bar::x is inaccessible. g++ 4.1.2 incorrectly accepts the invalid code (someone would need to test with a later version to see if it's been fixed; that's the only version I have on this laptop).
This seems like GCC bug 40843. It is listed as UNCONFIRMED, but I can reproduce it on g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3 as well.
VisualStudio 2010 said "error C2248: 'Bar::x' [...]
As the plateform was not speciifed, I have assessed that the assumption is false almost on Windows VC9.