The given code compiles in C but fails in C++.
int main()
{
const int x; /* uninitialized const compiles in C but fails in C++*/
}
What is the rationale and the reason behind the change from C to C++?
See the spec, in the compatibility appendix C.1.6:
7.1.6 [see also 3.5]
Change: const objects must be initialized in C++ but can be left uninitialized in C
Rationale: A const object cannot be assigned to so it must be initialized to hold a useful value.
Effect on original feature: Deletion of semantically well-defined feature.
Difficulty of converting: Semantic transformation.
How widely used: Seldom.
Note that there is a legitimate use of an uninitialized, const-qualified object of automatic storage duration: its address can be taken and used as a unique key for labeling recursion levels in a recursive function. This is somewhat obscure, but worth noting. C makes this use efficient, while C++ requires you waste time and code size on initializing it. (In theory the compiler could perhaps determine that the value is never used and optimize out the initialization, but since you're passing around a pointer, that would be rather difficult to prove.)
The const keyword was introduced to C in C89 in 1989, but had been with C++ since its creation in 1983. So it was "backported" from C++ to C.
Initialization semantics are generally different in C and C++. Although most of the time they "just do the thing you expect", there are cases where the differences become quite important. C++ really isn't a superset of C after all.
For example, in C++ you can't:
goto x;
int i = 3;
x:
puts("Hello, world");
But that is perfectly legal in C.
The ISO standard says (in 8.5 [dcl.init] paragraph 9):
If no initializer is specified for an object, and the object is of
(possibly cv-qualified) non-POD class type (or array thereof), the
object shall be default-initialized; if the object is of
const-qualified type, the underlying class type shall have a
user-declared default constructor.
if you try the same example after modifying to this:
int main()
{
/*Unless explicitly declared extern, a const object does not have
external linkage and must be initialized*/
extern const int x;
return 0;
}
it will get compiled. So this self explains the need of enforcing this error to c++, declaring const vars without initializing and extern linkage is of no use, so coder must have added it by mistake.
#include<iostream>
using namespace std;
class Test
{
int value;
public:
Test(int v = 0) {value = v;}
int getvalue() const {return value;}
};
int main(){
Test t(20);
cout << t.getvalue();
return 0;
const double P1 = 68.68;
cout<<P1<<endl;
int star = 57;
int const *pstar = ☆
cout<<*pstar<<endl;
return 0;
}
Related
const int test();
int test(){
return 5;
}
int main(){
return 0;
}
Above does not compile in C++ with this error message:
error: ambiguating new declaration of 'int test()'
However it does compile fine in C. Knowing these are 2 very different languages, I was wondering if there's a specific feature in C++ which requires it
to have const return types in both the definition and declaration?
I was wondering if there's a specific feature in C++ which requires it
to have const return types in both the definition and declaration?
No. Actually, it's the other way around. There is a specific feature in C that drops that requirement.
From C documentation of function declarations # cppreference.com:
The return type cannot be cvr-qualified: any qualified return type is adjusted to its unqualified version for the purpose of constructing the function type.
In your C example, even though you think you declared const int test();, the const qualification is dropped and you actually declared int test();. Hence declaration and definition match.
C++ does not drop cv-qualification from return types.
Yes, cv-qualified return type is different from a non-qualified return type, so those declarations conflict.
Note that it isn't ever useful to return a cv-qualified non-class object because the qualifiers will have no effect on the returned value. For classes, it does affect the type of the value and thus affects which member functions can be called (but cv qualified class return types are still rarely useful, if ever).
C++ has references.
In C you're always going to get a copy of the return value, so it doesn't matter so much if it's const or not.
int x;
x = test();
In C++ you can capture the return value with a reference, and it makes a big difference if the reference is const or not.
int &x = test();
const int &y = test();
So the compiler needs to know exactly what you're returning.
While writing a custom reflection library I encountered a strange compiler behavior. However I was able to reproduce the problem with a much simplified code. Here is:
#include <iostream>
class OtherBase{};
class Base{};
/* Used only as a test class to verify if the reflection API works properly*/
class Derived : Base, OtherBase
{
public:
void Printer()
{
std::cout << "Derived::Printer() has been called" << std::endl;
}
};
/*Descriptor class that basically incapsulate the address of Derived::Printer method*/
struct ClassDescriptor
{
using type = Derived;
struct FuncDescriptor
{
static constexpr const auto member_address{ &type::Printer };
};
};
int main()
{
Derived derived;
auto address{ &Derived::Printer };
(derived.*address)(); // -> OK it compiles fine using the local variable address
(derived.*ClassDescriptor::FuncDescriptor::member_address)(); // -> BROKEN using the address from the descriptor class cause fatal error C1001 !
}
While trying debugging this problem I noticed that:
It happen only if Derived has multiple inheritance.
If I swap static constexpr const auto member_address{ &type::Printer } with inline static const auto member_address{ &type::Printer } it works.
Is it just a compiler bug, or I'm doing something wrong ?
Can I solve this problem while keeping the constexpr ?
Please note that I'm using MSVC 2017 with the compiler version 19.16.27024.1
All compiler options are default except for /std:c++17 enabled.
I know that updating (and surely i'll do it) the compiler version to the last one will probably solve the issue, but for now I would like to understand more about this problem.
About C1001, Microsoft Developer Network suggests that you remove some optimizations in your code: Fatal Error C1001. Once you've worked out which optimization is causing the issue, you can use a #pragma to disable that optimization in just that area:
// Disable the optimization
#pragma optimize( "", off )
...
// Re-enable any previous optimization
#pragma optimize( "", on )
Also, a fix for this issue has been released by Microsoft. You could install the most recent release.
const and constexpr:
const declares an object as constant. This implies a guarantee that once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization.
constexpr declares an object as fit for use in what the Standard calls constant expressions. But note that constexpr is not the only way to do this.
When applied to functions the basic difference is this:
const can only be used for non-static member functions, not functions in general. It gives a guarantee that the member function does not modify any of the non-static data members.
constexpr can be used with both member and non-member functions, as well as constructors. It declares the function fit for use in constant expressions. The compiler will only accept it if the function meets certain criteria (7.1.5/3,4), most importantly:
The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single return statement is allowed. In the case of a constructor, only an initialization list, typedefs, and static assert are allowed. (= default and = delete are allowed, too, though.)
As of C++14, the rules are more relaxed, what is allowed since then inside a constexpr function: asm declaration, a goto statement, a statement with a label other than case and default, try-block, the definition of a variable of non-literal type, definition of a variable of static or thread storage duration, the definition of a variable for which no initialization is performed.
The arguments and the return type must be literal types (i.e., generally speaking, very simple types, typically scalars or aggregates)
When can I / should I use both, const and constexpr together?
A. In object declarations. This is never necessary when both keywords refer to the same object to be declared. constexpr implies const.
constexpr const int N = 5;
is the same as
constexpr int N = 5;
However, note that there may be situations when the keywords each refer to different parts of the declaration:
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
Here, NP is declared as an address constant-expression, i.e. a pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexpr and const are required: constexpr always refers to the expression being declared (here NP), while const refers to int (it declares a pointer-to-const). Removing the const would render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &N is in-fact a pointer-to-constant).
B. In member function declarations. In C++11, constexpr implies const, while in C++14 and C++17 that is not the case. A member function declared under C++11 as
constexpr void f();
needs to be declared as
constexpr void f() const;
under C++14 in order to still be usable as a const function.
You could refer to this link for more details.
In C++11 a new feature was introduced where the programmer can initialize class member variables inside class's definition, see code below:
struct foo
{
int size = 3;
int id = 1;
int type = 2;
unsigned char data[3] = {'1', '2', '3'};
};
Is this initialization takes place during compile time or this feature is just syntactic sugar and member variables are initialized in the default constructor?
First of all yes, as stated before, it is syntactic sugar. But since the rules can be too much to remember, here's a logical experiment to help you figure out what happens in compile time and what not
You have your c++11 class that features in class initializers
struct foo { int size = 3; };
And another class that will help us with our experiment
template<int N>
struct experiment { enum { val = N }; };
Let our hypothesis H0 be that initialization does happen in compile time, then we could write
foo a;
experiment<a.size> b;
No luck, we fail to compile. One could argue that failure is due to foo::size being non constant so lets try with
struct foo { const int size = 3; }; // constexpr instead of const would fail as well
Again, as gcc informs us
the value of ‘a’ is not usable in a constant expression
experiment b;
or (more clearly) visual studio 2013 tells us
error C2975: 'N' : invalid template argument for 'example', expected compile-time constant expression
So, we have to discard H0 and deduce that initialization does not happen in compile time.
What would it take to happen in compile time
There is an old syntax that does the trick
struct foo { static const int size = 3; };
Now this compiles but beware this is (technically and logically) no longer in class initialization.
I had to lie for a little to make a point, but to expose the whole truth now : Message errors imply that a is the real problem. You see, since you have an instance for an object (Daniel Frey also mentions this) memory (for members) has to be initialized (at runtime). If the member was (const) static, as in the final example, then it's not part of the subobjects of a(ny) class and you can have your initialization at compile time.
In-class initialisers for member-variables are syntactic sugar for writing them in the constructor initialiser list, unless there's an explicit initialiser already there, in which case they are ignored.
In-class initialisers of static const members are for constant literals, a definition is still needed (though without initialiser).
C++ has the "as if"-rule from C, so anything resulting in the prescribed observed behavior is allowed.
Specifically, that means static objects may be initialised at compile-time.
It's just syntactic sugar. Also consider that an instance usually means memory which has to be initialized with the correct values. Just because these values are provided with a different syntax does not change the fact that the memory needs to be initialized - which happens at run-time.
Its essentially syntactic sugar for a user provided constructor which initializes the values. You are providing default values for data members. When you ask whether this happens at compile time or run time, the answer depends on the context its used in.
Hopefully, these examples will help. Try them in http://gcc.godbolt.org and see the dissassembly and compilation errors.
struct S { int size = 3; };
//s's data members are compile time constants
constexpr S s = {};
//r's data members are run time constants
const S r = {};
//rr's data members are run time constants,
//but we don't know the values in this translation unit
extern const S rr;
template <int X> class Foo {};
//Ok, s.size is a compile time expression
Foo<s.size> f;
//Error, r.size is not a compile time expression
Foo<r.size> g;
//Compile time expression, this is same as return 3;
int foo() { return s.size; }
//This also works
constexpr int cfoo() { return s.size; }
//Compiler will optimize this to return 3; because r.size is const.
int bar() { return r.size; }
//Compiler cannot optimize, because we don't know the value of rr.size
//This will have to read the value of rr.size from memory.
int baz() { return rr.size; }
As others have shown, static data members (and global variables, same thing essentially) for primitive types such as ints and floats have some weird rules where they can be const but still be used in compile time contexts as if they were constexpr. This is for backwards compatibility with C and the lack of the constexpr feature in the past. Its unfortunate now because it just makes understanding constexpr and what differentiates run time expressions from compile time expressions more confusing.
Having int size = 3; is exactly equivalent to having int size; and then each constructor that doesn't already have size in its initializer list (including compiler-generated constructors) having size(3) there.
Strictly speaking C++ doesn't have a distinction between "compile-time" and "run-time".
I have one simple class like
class Person {
static const int MALE; // in Person.cpp initialized = 1
static const int FEMALE;//in Person.cpp initialized = 2
};
In Company class (Company.cpp file, I have company class) I have function with switch
switch(x){// x is int passed as parameter to function
case Person::MALE:
//do something
break;
case Person::FEMALE:
//do something
break;
}
but when I try to build I got error error C2051: case expression not constant for lines in case in switch above
What is a problem when it is a const ?
Change the declarations of static data members the following way
class Person {
static const int MALE = 1;
static const int FEMALE = 2;
};
The compiler must know the values of case labels during the compilation time.
A const is not a constant expression unless
It is a const variable of integral type, previously initialized with a literal or other constant expression.
C++11 adds constexpr, which can be used with variables of non-integral type, but the requirement for a prior initialization still applies.
Your problem is that in Company.cpp, this variable has not been initialized. The compiler will have to assume that the actual definition involves a runtime calculation. For example, it is perfectly legal to write const int Person::MALE = rand();
Or if Person.cpp contained
const int Person::MALE = 1;
const int Person::FEMALE = 1;
then the compiler would have to reject Company.cpp because the cases are not unique. How would that work? What if someone edited Person.cpp after Company.cpp was already compiled?
Values used in case expressions should be already known on compile-time, because they are somewhat "hardcoded" into binary code. And here they are specified only at a linkage phase. Probably the solution might be following:
// person.h
enum Person { MALE, FEMALE };
According to the C++11 standard: an expression is not a constant
expression if it contains an lvalue-to-rvalue conversion,
unless it is applied to "a glvalue of integral or enumeration
type that refers to a non-volatile const object with a preceding
initialization, initialized with a constant expression". (There
are some other cases, but they don't apply here.) Note the
requirement for a "preceding initialization"; not only must the
variable be const, but the compiler must be able to see its
initialization.
Earlier versions of the standard were somewhat vague in this
regard, and the natural interpretation of what they literally
say would suggest that your code be legal. This was certainly
not what was intended, however; no compiler implemented it in
this way (since it would generally require breaking separate
compilation), and C++11 clearly says that this is illegal.
C++ is not java. Use enums:
enum Person
{
Person_male = 1,
Person_female = 2
};
For example, let's consider the static storage class specifier. Here are a few examples of both valid and ill-formed uses of this storage class specifier:
static int a; // valid
int static b; // valid
static int* c; // valid
int static* d; // valid
int* static e; // ill-formed
static int const* f; // valid
int static const* g; // valid
int const static* h; // valid
int const* static i; // ill-formed
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
(The declarations marked "valid" were accepted by Visual C++ 2012, g++ 4.7.2, and Clang++ 3.1. The declarations marked "ill-formed" were rejected by all of those compilers.)
This seems odd because the storage class specifier applies to the declared variable. It is the declared variable that is static, not the type of the declared variable. Why are e and i ill-formed, but k is well-formed?
What are the rules that govern valid placement of storage class specifiers? While I've used static in this example, the question applies to all storage class specifiers. Preferably, a complete answer should cite relevant sections of the C++11 language standard and explain them.
In summary, anywhere in the declaration specifier (See section 7.1 in the ISO/IEC 14882-2012), ie before the *. Qualifiers after the * are associated with the pointer declarator, not the type specifier, and static doesn't make sense within the context of a pointer declarator.
Consider the following cases:
You can declare a normal int and a pointer to an int in the same declaration list, like this:
int a, *b;
this is because the type specifier is int, then you have two declarations using that type specifier int, a, and a pointer declarator *a which declares a pointer to int. Now consider:
int a, static b; // error
int a, *static b; // error
int a, static *b; // error
which should look wrong (as they are), and the reason (as defined in sections 7.1 and 8.1) is because C and C++ require that your storage specifiers go with your type specifier, not in your declarator.
So now it should be clear that that the following is also wrong, since the above three are also wrong:
int *static a; // error
Your last example,
typedef int* pointer;
static pointer j; // valid
pointer static k; // valid
are both valid and both equivalent because the pointer type is defined as a type specifier and you can put your type specifier and storage specifeir in any order. Note that they are both equivalent and would be equivalent to saying
static int *j;
static int *k;
or
int static *j;
int static *k;
Per 7.1, the [simplified] structure of C++ declaration is
decl-specifier-seq init-declarator-list;
Per 7.1/1, storage class specifiers belong in the initial "common" part decl-specifier-seq.
Per 8/1, init-declarator-list is a sequence of declarators.
Per 8/4, the * part of pointer declaration is a part of an individual declarator in that sequence. This immediately means that everything that follows a * is a part of that individual declarator. This is why some of your storage class specifier placements are invalid. Declarator syntax does not allow inclusion of storage class specifiers.
The rationale is rather obvious: since storage class specifiers are supposed to apply to all declarators in the whole declaration, they are placed into the "common" part of the declaration.
I'd say that a more interesting (and somewhat related) situation takes place with specifiers that can be present in both decl-specifier-seq and individual declarators, like const specifier. For example, in the following declaration
int const *a, *b;
does const apply to all declarators or only to the first one? The grammar dictates the former interpretation: that const applies to all declarators, i.e. it is a part of the decl-specifier-seq.
If you employ the "Golden Rule" (which also doesn't apply only to pointers) it follows naturally, intuitively, and it avoids a lot of mistakes and pitfalls when declaring variables in C/C++. The "Golden Rule" should not be violated (there are rare exceptions, like const applied to array typedefs, which propagates const to the base type, and references, that came with C++).
K&R, Appendix A, Section 8.4, Meaning of Declarators states:
Each declarator is taken to be an assertion that when a construction of the same form as the declarator appears in an expression, it yields an object of the indicated type and storage class.
To declare a variable in C/C++ you should really think of the expression you should apply to it to get the base type.
1) There should be a variable name
2) Then comes the expression as valid* out of the declaration statement, applied to the variable name
3) Then comes the remaining information and properties of declaration like base type and storage
Storage is not a characteristic you can always confer to the outcome of expressions, contrary to constness for example. It makes sense only at declaration. So storage must come somewhere else that's not in 2.
int * const *pp;
/*valid*/
int * static *pp;
/*invalid, this clearly shows how storage makes no sense for 2 and so breaks */
/*the golden rule. */
/*It's not a piece of information that goes well in the middle of a expression.*/
/*Neither it's a constraint the way const is, it just tells the storage of */
/*what's being declared. */
I think K&R wanted us to use inverted reasoning when declaring variables, it's frequently not the common habit. When used, it avoids most of complex declaration mistakes and difficulties.
*valid is not in a strict sense, as some variations occur, like x[], x[size, not indexing], constness, etc... So 2 is a expression that maps well (for the declaration usage), "same form", one that reflects variable's use, but not strictly.
Golden Rule Bonus for the Uninitiated
#include <iostream>
int (&f())[3] {
static int m[3] = {1, 2, 3};
return m;
}
int main() {
for(int i = 0; i < sizeof(f()) / sizeof(f()[0]); ++i)
std::cout << f()[i] << std::endl;
return 0;
}
In the context of declarations, & is not an operation to get an address, it just tells what's a reference.
f(): f is a function
&return: its return is a reference
reference[3]: the reference is to an array of 3 elements
int array[i]: an element is an int
So you have a function that returns a reference to an array of 3 integers, and as we have the proper compile time information of the array size, we can check it with sizeof anytime =)
Final golden tip, for anything that can be placed before the type, when in multiple declarations, it's to be applied to all the variables at once, and so can't be applied individually.
This const can't be put before int:
int * const p;
So the following is valid:
int * const p1, * const p2;
This one can:
int const *p; // or const int *p;
So the following is invalid:
int const *p1, const *p2;
The exchangeable const is to be applied for all:
int const *p1, *p2; // or const int *p1, *p2;
Declaration Conventions
Because of that, I always put everything that can't be put before the type, closer to the variable (int *a, int &b), and anything that can be put before, I put before (volatile int c).
There's much more on this topic at http://nosubstance.me/post/constant-bikeshedding/.