Why does the first commented line compile correctly, whereas the second doesn't?
Why can a be given itself as a constructor argument, but b can't?
Aren't the two doing the same thing?
class Foo { Foo &operator =(Foo const &); /* Disable assignment */ };
int main()
{
Foo a = a; // OK
Foo b(b); // error C2065: 'b' : undeclared identifier
}
Update
Since it seems like it's compiler-dependent, it seems like the problem is more severe than I thought.
So I guess another part of the question is, is the following code valid or no?
It gives an error in GCC but Visual C++ executes it just fine.
int main()
{
int i = 0;
{ int *i(&i); }
return i;
}
In your first code, both declarations should compile. GCC is right there. Visual C++ Compiler has bug.
And in the second code, the inner declaration should not compile. GCC is right there too, and VC++ is wrong.
GCC is right in both cases.
A code like int a=a+100; and int a(a+100); is fine from syntax point of view. They might invoke undefined behavior depending on whether they're created in static storage duration or automatic storage duration.
int a = a + 100; //well-defined. a is initialized to 100
//a on RHS is statically initialized to 0
//then a on LHS is dynamically initialized to (0+100).
void f()
{
int b = b + 100; //undefined-behavior. b on RHS is uninitialized
int a = a + 50; //which `a` is on the RHS? previously declared one?
//No. `a` on RHS refers to the newly declared one.
//the part `int a` declares a variable, which hides
//any symbol with same name declared in outer scope,
//then `=a+50` is the initializer part.
//since a on RHS is uninitialized, it invokes UB
}
Please read the comments associated with each declaration above.
Note that variables with static storage duration is statically initialized to zero at compile time, and if they've initializer, then they're dynamically initialized also at runtime. But variables of POD types with automatic storage duration are not statically initialized.
For more detail explanation on static initialization vs dynamic initialization, see this:
What is dynamic initialization of object in c++?
In your first example, as you note, the behavior is undefined even though the syntax is okay. A compiler is therefore permitted to refuse the code (the undefined behavior must be guaranteed however; it is here, but it wouldn't be if the invalid initializations were never actually executed).
Your second example has a type error: A declaration is visible as soon as its declarator is seen, and in particular it is visible in its own initializer. MSVC++ delays the visibility: That's a known non-conformance issue in that compiler. E.g., with the EDG compiler (which has a Microsoft mode):
$ ./cfe --strict x.c
"x.c", line 4: error: a value of type "int **" cannot be used to initialize an
entity of type "int *"
{ int *i(&i); }
^
1 error detected in the compilation of "x.c".
$ ./cfe --microsoft x.c
"x.c", line 4: warning: variable "i" was declared but never referenced
{ int *i(&i); }
^
Related
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++, to declare an object of a class that has a member variable as const, we must have a user-defined default constructor. The following code illustrates this.
class Some {
int value;
};
int main() {
// error: default initialization of an object of const type 'const Some'
// without a user-provided default constructor
const Some some;
return 0;
}
However, if a member variable owned by a class is qualified as mutable, the compiler will not report any errors. For reference, I compiled using the command clang++ -std=c++17 -stdlib=libc++ helloworld.cpp -o helloworld.out --debug. I wonder if this result is due to a bug in the compiler or according to the syntax defined in the C++ language.
class Some {
mutable int value;
};
int main() {
const Some some;
return 0;
}
Rewriting my comment as an answer, hope it could helps someone.
It makes no sense declaring a const object if it is not initialized in some form.
Consider the following code:
const int x;
clang says: error: default initialization of an object of const type 'const int'.
gcc would say: error: uninitialized const ‘x’ [-fpermissive]
The logic behind this is that there is no sense in this type of declaration.
The value of x can never change, and therefore this code would be unpredictable as x would be mapped to uninitialized memory.
In your example, adding the keyword mutable to value means that although the Some instance is constant when declared as:
const Some some;
It is still possible to change value at a later time.
For example:
some.value = 8;
This means it is possible to use this code in a predictable manner, since value can be set later, and there are no uninitialized constants.
A constexpr function is defined as (c++14)
A constexpr function must satisfy the following requirements:
it must not be virtual
its return type must be LiteralType each of its parameters must be LiteralType
there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a
core constant expression (for constructors, use in a constant
initializer is sufficient) (since C++14). No diagnostic is required
for a violation of this bullet.
the function body must be either deleted or defaulted or contain any
statements except:
an asm declaration
a goto statement
a statement with a label other than case and default
a try-block
a definition of a variable of non-literal type
a definition of a variable of static or thread storage duration
a definition of a variable for which no initialization is performed.
Now following func1 meets the requirement and compiles
constexpr int * func1 (int a)
{
int b = 4;
return &b;
}
int main()
{
constexpr int * a = func1(3);
int arr[*a];
std::cout << a << std::endl;
}
Now my question is how come func1 is constexpr. How does it know address of local variable at compile time?
I am using gcc 6.4.0
How does it know address of local variable at compile time?
It doesn't.
The third bullet in your quote is never satisfied:
There exists at least one set of argument values such that an
invocation of the function could be an evaluated subexpression of a
core constant expression (for constructors, use in a constant
initializer is sufficient) (since C++14). No diagnostic is required
for a violation of this bullet.
The compiler just doesn't complain about it because it's not required to, until you make it complain by trying to use func1 inside something that requires a correct constexpr function, for example:
std::array<int, func(3)> d;
This won't compile and your compiler will tell you why.
Now my question is how come func1 is constexpr.
Are you sure?
Try asking a compile time value saving it in a constexpr variable; by example
constexpr int * a = func1(3);
You should get a list of error/warnings like (from my clang++ 3.8.1)
tmp_003-14,gcc,clang.cpp:7:11: warning: address of stack memory associated with
local variable 'b' returned [-Wreturn-stack-address]
return &b;
^
tmp_003-14,gcc,clang.cpp:11:21: error: constexpr variable 'a' must be
initialized by a constant expression
constexpr int * a = func1(3);
^ ~~~~~~~~
tmp_003-14,gcc,clang.cpp:11:21: note: pointer to 'b' is not a constant
expression
tmp_003-14,gcc,clang.cpp:6:7: note: declared here
int b = 4;
^
Well, actually, from my g++ 6.3.0 I get only a warning
tmp_003-14,gcc,clang.cpp: In function ‘constexpr int* func1(int)’:
tmp_003-14,gcc,clang.cpp:7:7: warning: address of local variable ‘b’ returned [-Wreturn-local-addr]
int b = 4;
^
In your code, when func1() returns, b goes out of scope.Thus, any usage relating to b outside func1() is undefined behavior.
constexpr int * func1 (int a)
{
int b = 4; // remember: b is non-static
return &b;
}
int * a = func1(3); // b is out of scope here
Also, from expr.const:
A core constant expression satisfies:
(5.2) if the value is of pointer type, it contains the address of an
object with static storage duration, the address past the end of
such an object, the address of a function, or a null pointer value
Which in your case, b is not static, which means func1() is not a constant expression.
When I try to use a static const to initialize a unique_ptr, I get an "undefined reference" error. However, when I new a pointer using the same constant, the symbol seems to be magically defined.
Here is a simple program that reproduces the error:
Outside_library.h
class Outside_library
{
public:
static const int my_const = 100;
};
main.cpp
#include "Outside_library.h"
#include <iostream>
#include <memory>
class My_class
{
public:
My_class(int num)
{
m_num = num;
};
virtual ~My_class(){};
private:
int m_num;
};
int main(int, char* [])
{
My_class* p_class = new My_class(Outside_library::my_const);
delete p_class;
// ==== ERROR HERE: When I comment this line out, the program runs fine.
std::unique_ptr<My_class> p_unique
= std::make_unique<My_class>(Outside_library::my_const);
std::cout << "I made it through!" << std::endl;
return 0;
}
I compiled the program using
g++ main.cpp -std=c++14
and got the following error.
/tmp/ccpJSQJS.o: In function `main':
main.cpp:(.text+0x51): undefined reference to `Outside_library::my_const'
collect2: error: ld returned 1 exit status
Can someone please help me understand why the constant is defined when using new, but not when using make_unique?
C++ has something known as the One-Definition Rule (ODR):
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; a reference is odr-used if it is used and its referent is not known at compile time; and a function is odr-used if a function call to it is made or its address is taken. If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error.
The linked site gives the following example:
struct S {
static const int x = 0; // static data member
// a definition outside of class is required if it is odr-used
};
const int& f(const int& r);
int n = b ? (1, S::x) // S::x is not odr-used here
: f(S::x); // S::x is odr-used here: a definition is required
Your explicit constructor invocation does not "odr-use" Outside_library::my_const but the call to std::make_unique() does. When an object is odr-used it must have exactly one definition (not declaration). Your example has a declaration only. Again from cppreference:
a variable x in a potentially-evaluated expression ex is odr-used unless both of the following are true:
applying lvalue-to-rvalue conversion to x yields a constant expression that doesn't invoke non-trivial functions
either x is not an object (that is, x is a reference) or, if x is an object, it is one of the potential results of a larger expression e, where that larger expression is either a discarded-value expression or has the lvalue-to-rvalue conversion applied to it
The solution as suggested by Jarod42 is to use constexpr instead of const (if you have control over the "outside library" code). If you do not, then you'll need to link the program against the library that contains the definition of Outside_library::my_const.
g++ main.cpp -std=c++14 -lOutside_library
make_unique takes (forwardind) reference of its parameter, and so odr-use it.
My_class(Outside_library::my_const) only use the value.
One solution is to define the member in a TU:
const int Outside_library::my_const;
or using constexpr value (since C++11):
class Outside_library
{
public:
static constexpr int my_const = 100;
};
I bumped against this error in some code, and after some experimenting I stumbled upon this weirdness - I get it for std::string, but not for int.
For std::string I get error C2362: initialization of 'unused' is skipped by 'goto label':
{ goto label;
std::string unused;
label:;
}
For int I don't get any error, however:
{ goto label;
int unused = 10;
label:;
}
Why the difference? Is it because std::string has a non-trivial destructor?
This is covered in the draft C++ standard section 6.7 Declaration statement which says (emphasis mine):
It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps87 from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).
and provides the following example:
void f() {
// ...
goto lx; // ill-formed: jump into scope of a
ly:
X a = 1;
// ...
lx:
goto ly; // OK, jump implies destructor
// call for a followed by construction
// again immediately following label ly
}
Although both cases should generate an error since your are bypassing an initialization in both cases, this however would have been fine:
goto label;
int unused ;
label:
So Visual Studio is not correct here, both gcc and clang generate and error for this code, gcc says:
error: crosses initialization of 'int unused'
int unused = 10;
^
Of course Visual Studio can have extension like that as long as they document it but it is not portable to use such an extension, as I pointed out both clang and gcc generate an error for this.
We can find a rationale for why we don't want to jump across an initialization in defect report 467 which sought to have the same restriction added for local static variable (it was rejected):
[...]automatic variables, if not explicitly initialized, can have indeterminate (“garbage”) values, including trap representations, [...]
A compiler error. Both are illegal. What is not illegal,
however, is:
{
goto label;
int unused;
unused = 10;
label:
;
}
Both std::string unused; and int unused = 10; have
initializers (a default constructor in the case of
std::string), and you're not allowed to jump around
a definition with an initializer. Jumping around one without an
initializer is probably allowed avoid breaking code like:
switch ( something )
{
int i;
case 0:
i = x;
// ...
break;
case 1:
i = y;
// ...
break;
// ...
}
I wouldn't call this good code, but it wouldn't surprise me to
find it in older C, and C++ does try not to break these sort of
things.