I understand that static const members have to have out-ouf-class definition it they are odr-used. But the thing is, my program compiles and run just fine even without members definition.
Let's take a look at this example from C++ FAQ:
class AE
{
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
void f()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
cout << *p1 << endl;
}
int main()
{
f();
const int* p1 = &AE::c6;
std::cout << p1 << "\n";
return 0;
}
//RESULT:
// 7
// 00007FF735E7ACE8
I see no error whatsoever. I use Visual Studio 2015, and this code compiles and runs just fine.
My question: Is this specific to msvc or there are some language changes that I'm not aware of?
UPDATE: This is not a duplicate, as I said at the very beginning: I do understand how this suppose to work, I don't understand why it doesn't work as it should.
The program explicitely violates the one definition rule as you have stated in your question. But the standard does not require a diagnostic in this case. This is explicit in 3.2 One definition rule [basic.def.odr] §4 (emphasis mine)
Every program shall contain exactly one definition of every non-inline fonction or variable that is odr-used
in that program; no diagnostic required.
and 1.4 Implementation compliance [intro.compliance] §2.3 says:
If a program contains a violation of a rule for which no diagnostic is required, this International
Standard places no requirement on implementations with respect to that program.
That means that gcc is right to choke on that program because one rule has been violated. But it is not a bug when MSVC accepts it as a compiler extension(*) without even a warning because the standard places no requirement here.
We are in the compile time undefined behaviour named ill-formed program, no diagnostic required. A compiler implementation is free to do what it wants:
reject the program
automatically fix the error and generate the code as if the definition was present (for current case)
remove the offending line[s] if it makes sense and makes the program compilable - even if what is generated is not what the programmer expected
compile it into a program that ends in a run-time error
[add whatever you think of, but I have never seen a compiler able to hit my cat...]
(*) more exactly, it is a compiler extension if it is documented. Unfortunately, I currently have no MSVC compiler full documentation, so I can not say whether it is documented and is a conformant extension, or is not and is just hmm... one possible execution of the program
Here is a slight variation on OP's code demonstrating that the compiler did provide a definition:
#include <iostream>
class AE {
// ...
public:
static const int c6 = 7;
static const int c7 = 31;
};
const int AE::c7; // definition
int main()
{
const int* p1 = &AE::c6; // error: c6 not an lvalue
const int* p2 = &AE::c7; // ok
// ...
std::cout << *p1 << "(" << p1 << ") - " << *p2 << "(" << p2 << ")" << std::endl;
return 0;
}
The output is (with an old MSVC 2008, debug mode to avoid as much optimization as possible):
7(00DF7800) - 31(00DF7804)
that is expected values and consecutive addresses
With same code, Clang 3.4 complains (as expected) at link time with
undefined reference to `AE::c6'
It just so happens that your compiler has applied some optimisations that mean it doesn't require you to follow the One Definition Rule in this case.
However, the C++ standard still requires you to do so, so your program has undefined behaviour. The standard does not require a compiler to tell you when you violate this particular rule, so it's chosen not to. Indeed, it would have to put in extra effort to detect the problem.
The C++ standard says ([basic.def.odr] 3.2 paragraph 2) "A variable
whose name appears as a potentially-evaluated expression is odr-used
unless it is an object that satisfies the requirements for appearing
in a constant expression (5.19) and the lvalue-to-rvalue conversion
(4.1) is immediately applied."
Read this for further information
Related
I've been experimenting with different forms of UB in order to better understand the current compiler inadequacies using constexpr evaluations to detect UB.
Note: There is normally no compiler requirement to emit diagnostics when UB occurs. However, this is not the case when executing compile time code such as consteval functions as shown here. Even there, there is an exception for library code where compilers are not required to emit diagnostics for UB. The question here does not use library code and compilers should emit diagnostics when encountering UB executing code at compile time.
I ran across an odd situation where modifying the underlying memory of an const int in automatic storage (aka stack) results in no warnings with CLANG, GCC, and MSVC but when evaluated in a constexpr function CLANG and GCC elicit the expected error while MSVC gives no error.
The function being evaluated casts a const int to a non const through it's address to attempt to change it's value from 1 to 2. Afterwards, the const value is added to that returned from a function called with a const int& that simply returns the argument value.
This is all UB, of course. However, all three compilers happily compile and run when not in a constexpr function. While UB, they are not required to emit diagnostics. And they interpret the result as the original const value when the variable is used directly but as the modified value when accessed through a reference.
But when running in compile time code, GCC and CLANG identify the UB as, for example:
modification of object of const-qualified type 'const int' is not allowed in a constant expression
MSVC produces no error or warning.
Since this uses no library code in the constexpr evaluation, I believe MSVC is in error not reporting the UB. Is this correct?
Compiler Explorer
#include <iostream>
// un-comment out the next line to evaluate at runtime
// #define consteval
consteval int ret_arg(const int& v) { return v; }
consteval int f()
{
const int i{ 1 };
*const_cast<int*>(&i) = 2;
return i + ret_arg(i);
}
int main()
{
std::cout << f() << "\n";
}
Run time UB w/o compiler errors for all compilers can be seen by commenting the "#define consteval" line at the top.
I am curious why there is a difference in the argument evaluation order between chained static functions and member functions. From the answers at this question I can see it is unspecified what the argument evaluation order is between such chained function calls. Take for example the following snippet:
#include <iostream>
class test {
public:
static test& chain_s(test& t, int i) {
std::cout << i << " ";
return t;
}
test& chain(test& t, int i) {
std::cout << i << " ";
return *this;
}
};
int main(int, char**) {
int x = 2;
test t;
t.chain(t,++x).chain(t,++x).chain(t,++x);
x = 2; std::cout << std::endl;
t.chain_s(t,++x).chain_s(t,++x).chain_s(t,++x);
return 0;
}
In the case of GCC 4.6.2 and CL 15.00.30729.01 (MSVC 9) the resulting output is for me
5 5 5
3 4 5
However, I was wondering if there is any reason in the specification or if it is otherwise known why the static function are evaluated left-to-right (with their arguments), and for the non-static function all the arguments first (right-to-left from what I've seen in other tests).
The reason I'm asking this is because I first noticed this difference in behavior when trying to get similar behavior in C (using a struct and a function pointer) and failed. I strongly suspect this is some optimization implemented both in GCC and MSVC for member functions, but I hope someone here can shed a little more light on this.
Edit:
I forgot to mention one crucial bit of information which strikes me as odd: GCC will only warn on unspecified behavior on the chained non-static function, but not the static functions:
a.cpp: In function 'int main(int, char**)':
a.cpp:18:45: warning: operation on 'x' may be undefined [-Wsequence-point]
GCC is not obligated to provide such warnings so it could miss the second expression, but this is what leads me to believe something interesting is going on.
No reason. Like you say, the order is unspecified by the language.
One reason for using right to left order is that functions with a variable number of parameters, like printf, will then always have the first parameter on top. Otherwise it doesn't matter.
Your code has undefined behavior, but I suppose you know that. Also,
you could easily see a difference depending on optimization flags. But
in this case, one likely reason is that the non-static functions require
three arguments, including the results of the previous call, where as
the static functions only require two, and the results of the previous
call are ignored.
I am a bit confused by the static in-class initialization of a const member. For example, in the code below:
#include <iostream>
struct Foo
{
const static int n = 42;
};
// const int Foo::n; // No ODR
void f(const int& param)
{
std::cout << param << std::endl;
}
int g(const int& param)
{
return param;
}
template<int N>
void h()
{
std::cout << N << std::endl;
}
int main()
{
// f(Foo::n); // linker error, both g++/clang++
std::cout << g(Foo::n) << std::endl; // OK in g++ only with -O(1,2 or 3) flag, why?!
h<Foo::n>(); // this should be fine
}
Live example
I do not define Foo::n (the line is commented). So, I expect the call f(Foo::n) to fail at link time, and indeed it does. However, the following line std::cout << g(Foo::n) << std::endl; compiles and links fine only by gcc (clang still emits a linker error) whenever I use an optimization flag such as -O1/2/3.
Why does gcc (tried with gcc5.2.0 and gcc 4.9.3) compile and link the code when the optimization is turned on?
And am I correct to say that the only usage of in-class static const members is in constant expressions, such as template parameters like in the h<Foo::n> call, in which case the code should link?
I suppose that the compiler performs the following actions during the optimization:
The value const static int n is inlined everywhere. No memory is allocated for the variable n, references to it becomes invalid. The function f() need a reference to n so the program is not compiled.
The function g is short and simple. It is effectively inlined and optimized. After the optimization, the function g does not need a reference to n, it just returns constant value 42.
The solution is to define the variable outside the class:
struct Foo
{
const static int n;
};
const int Foo::n = 42;
ODR violations do not require a diagnostic, from the draft C++ standard standard section 3.2 [basic.def.odr] (emphasis mine going forward):
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required.
So inconsistent behavior at different optimization levels is perfectly conformant behavior.
Informally a variable is odr-used if:
its address is taken, or a reference is bound to it, and a function is odr-used if a function call to it is made or its address is taken. If an object or a function is odr-used, its definition must exist somewhere in the program; a violation of that is a link-time error.
So both f and g will be odr-uses and require a definition.
The relevant C++14 quote on odr-use would be from section [basic.def.odr]:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying
the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.19) that does not invoke any nontrivial
functions and, if x is an object, ex is an element of the set of potential results of an expression e,
where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression [...]
The wording in C++11 is similar, the changes from C++11 to C++14 are reflected in defect report 712.
Before C++11 it is a bit more complicated but in principle the same for this case.
Formally, ODR violations are undefined behaviour, so the compiler may exhibit any behaviour it likes. That's why the behaviour changes with optimization level and compiler- the compiler has no obligation to maintain a particular behaviour.
There is no definition at all. GCC 4.9.2 doesn't compile and link that with any flags.
Note, that:
const static int n = 42;
is a declaration and initializer, but not a definition.
As an interesting follow-up (not of big practical importance though) to my previous question:
Why does C++ allow us to surround the variable name in parentheses when declaring a variable?
I found out that combining the declaration in parentheses with injected class name feature may lead to surprising results regarding compiler behavior.
Take a look at the following program:
#include <iostream>
struct B
{
};
struct C
{
C (){ std::cout << "C" << '\n'; }
C (B *) { std::cout << "C (B *)" << '\n';}
};
B *y = nullptr;
int main()
{
C::C (y);
}
Compiling with g++ 4.9.2 gives me the following compilation error:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
It compiles successfully with MSVC2013/2015 and prints C (B *)
It compiles successfully with clang 3.5 and prints C
So obligatory question is which one is right? :)
(I strongly swayed towards clang version though and msvc way to stop declaring variable after just changing type with technically its typedef seems kind of weird)
GCC is correct, at least according to C++11 lookup rules. 3.4.3.1 [class.qual]/2 specifies that, if the nested name specifier is the same as the class name, it refers to the constructor not the injected class name. It gives examples:
B::A ba; // object of type A
A::A a; // error, A::A is not a type name
struct A::A a2; // object of type A
It looks like MSVC misinterprets it as function-style cast expression creating a temporary C with y as a constructor parameter; and Clang misinterprets it as a declaration of a variable called y of type C.
G++ is correct as it gives an error. Because the constructor could not be called directly in such a format without new operator. And although your code calls C::C, it looks like an constructor call. However, according to the C++11 standard 3.4.3.1, this is not a legal function call, or a type name (see Mike Seymour's answer).
Clang is wrong since it even does not call the correct function.
MSVC is something reasonable, but still it does not follow the standard.
I am curious why there is a difference in the argument evaluation order between chained static functions and member functions. From the answers at this question I can see it is unspecified what the argument evaluation order is between such chained function calls. Take for example the following snippet:
#include <iostream>
class test {
public:
static test& chain_s(test& t, int i) {
std::cout << i << " ";
return t;
}
test& chain(test& t, int i) {
std::cout << i << " ";
return *this;
}
};
int main(int, char**) {
int x = 2;
test t;
t.chain(t,++x).chain(t,++x).chain(t,++x);
x = 2; std::cout << std::endl;
t.chain_s(t,++x).chain_s(t,++x).chain_s(t,++x);
return 0;
}
In the case of GCC 4.6.2 and CL 15.00.30729.01 (MSVC 9) the resulting output is for me
5 5 5
3 4 5
However, I was wondering if there is any reason in the specification or if it is otherwise known why the static function are evaluated left-to-right (with their arguments), and for the non-static function all the arguments first (right-to-left from what I've seen in other tests).
The reason I'm asking this is because I first noticed this difference in behavior when trying to get similar behavior in C (using a struct and a function pointer) and failed. I strongly suspect this is some optimization implemented both in GCC and MSVC for member functions, but I hope someone here can shed a little more light on this.
Edit:
I forgot to mention one crucial bit of information which strikes me as odd: GCC will only warn on unspecified behavior on the chained non-static function, but not the static functions:
a.cpp: In function 'int main(int, char**)':
a.cpp:18:45: warning: operation on 'x' may be undefined [-Wsequence-point]
GCC is not obligated to provide such warnings so it could miss the second expression, but this is what leads me to believe something interesting is going on.
No reason. Like you say, the order is unspecified by the language.
One reason for using right to left order is that functions with a variable number of parameters, like printf, will then always have the first parameter on top. Otherwise it doesn't matter.
Your code has undefined behavior, but I suppose you know that. Also,
you could easily see a difference depending on optimization flags. But
in this case, one likely reason is that the non-static functions require
three arguments, including the results of the previous call, where as
the static functions only require two, and the results of the previous
call are ignored.