Rules for int[] array initialization from initializer list - c++

I would appreciate pointers to the int array initialization list in the C++ standard, I found an inconsistency between clang and gcc (c++ 17), see below and in compiler explorer.
https://godbolt.org/z/3vMfTv8Ts
Questions:
Why can v1 be initialized with a initializer list, but we can't initialize a temporary to pass it to Test2?
Why clang and gcc have different behavior when the function param is a reference to the int array?
Note: clang C++20 compiles the Test1 call.
I would appreciate the pointer to the C++ standard where these rules are described.
#include <iostream>
int Test1(const int (&a)[]) {
return a[0];
}
int Test2(const int a[]) {
return a[0];
}
int main() {
const int v1[] = { 1,2,3 };
std::cout << Test2(v1) << std::endl;
std::cout << Test2({ 1,2,3 }) << std::endl; // Does not compile in clang/gcc
std::cout << Test1({ 1,2,3 }) << std::endl; // Does not compile in clang
return 0;
}

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.

Constant reference data member

Let's suppose we have a structure with a constant reference data member.
struct A {
A() : i{5} {}
const int& foo() const { return i; }
const int& i;
};
Do you have any idea why the output for an integer literal 5 is different?
A a{};
std::cout << a.i << std::endl;
std::cout << a.foo() << std::endl;
5
-858993460
The code is ill-formed. You're initializing i from literal 5, which requires a temporary object to be contructed and then bound to i. The temporary will be destroyed when the constructor exits, then i becomes dangled, any dereference on it later leads to UB, means anything is possible.
From the standard, [class.base.init]/8
A temporary expression bound to a reference member in a
mem-initializer is ill-formed. [ Example:
struct A {
A() : v(42) { } // error
const int& v;
};
— end example ]
BTW: Since the standard states it's ill-formed, the compilers are required to issue a diagnostic for it. Both the behavior of gcc (gives a warning) and clang (gives an error) are conforming; if VS2017 doesn't issue any diagnostic then it's non-conforming to the standard.
Use of a constant value not a literal.
This is Class::Class() : member{arg1, arg2, ...} {... direct-list-initialization since c++11.
Try this :
struct A {
const int t = 5;
A() : i{ t } { }
const int& foo() const { return i; }
const int& i;
};
int main()
{
A a{};
std::cout << a.i << std::endl;
std::cout << a.foo() << std::endl;
return 0;
}

Where in the Standard does it say that the default member initializer for U::j should be ignored by the compiler?

Consider the following snippet:
#include <iostream>
union U{
U(): i(1) {}
int i;
int j = 2; // this default member initializer is ignored by the compiler
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
The code prints (see live example):
1
1
Where in the Standard does it say that the default member initializer for the member U::j is ignored by the compiler?
Note that the union below doesn't compile and this is OK according to [class.union.anon]/4. I was thus expecting the snippet above also not to compile.
See live example:
union U{
int i = 1;
int j = 2;
};
Where in the Standard does it say that the default member initializer for the member U::j is ignored by the compiler?
See [class.base.init] paragraph 9 bullet 9.1 in the C++17 CD.
N.B. your demo has undefined behaviour, because the active member of the union is i but you read from j. This works with some compilers as a non-standard extension, but is not allowed in ISO C++.
Note that you are declaring a union object, where all members share the same memory area - member variables turn into different "typed views" of the same data.
Thus, as members i and j are effectively stored in the same memory location, any initialization you perform on j (with the initializer) will be overwritten by your constructor setting i.
Just for test, remove the initialization of i from the constructor:
#include <iostream>
union U{
U() {}
int i;
int j = 2; // this initializes both i & j
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
The output would be
2
2
Update:
As per #Ayrosa comments and being just intrigued, I modified the original snippet to perform some initialization with a function (instead of a constant) to induce side effects.
#include <iostream>
int static someStatic()
{
std::cout << "Initializer was not ignored\n";
return(2);
}
union U{
U(): i(1) {}
int i;
int j = someStatic(); // this default member initializer is ignored by the compiler
};
U u;
int main(){
std::cout << u.i << '\n';
std::cout << u.j << '\n';
}
Result was:
1
1
Meaning that the call to someStatic() was, in fact, ignored by the compiler.

why put dozens of * before function pointer or function that return a function pointer compliles?

I have found that this code dealing with multiple function pointer dereferences unexpectedly compiles, despite seeming not to be valid. How can this compile? Is it a bug in the compiler? I am using gcc 4.8.2 on Ubuntu 14.04.
int addInt(int n,int m) // function
{
return n+m;
}
int (*(*functionFactoryPtr)(int n))(int, int); // pointer
int (*(functionFactory)(int n))(int, int) // function
{
std::cout << "Got parameter" << n << std::endl;
int (*functionPtr)(int,int) = &addInt;
return functionPtr;
}
int main()
{
// functionFactoryPtr = #functionFactory;
std::cout << (******(*****functionFactory)(4))(3,6) << std::endl; // How is this not an error?
}
A function lvalue is implicitly convertible to a pointer to the function ([conv.func]). In your example, the function is converted to a pointer before each dereference.

Initial value of primitive data-types

#include <iostream>
class MyClass
{
public:
MyClass() :
mFirst()
{
}
int mFirst;
int mSecond;
};
int main()
{
MyClass mc;
std::cout << "mc.mFirst: " << mc.mFirst << std::endl;
std::cout << "mc.mSecond: " << mc.mSecond << std::endl;
int a;
std::cout << "a: " << a << std::endl;
return 0;
}
What is the expected output of this program?
I would think only MyClass.mFirst will be initialized to zero. However GCC initializes them all to zero, even with optimizations enabled:
$ g++ -o test -O3 main.cpp
$ ./test
mc.mFirst: 0
mc.mSecond: 0
a: 0
I'd like to know:
How is each value initialized according to the C++ standard?
Why does GCC initialize them all to zero?
Update
According to Erik the values are zero because my stack happens to contain zeroes. I tried forcing the stack to be non-zero using this construct:
int main()
{
// Fill the stack with non-zeroes
{
int a[100];
memset(a, !0, sizeof(a));
}
MyClass mc;
std::cout << "mc.mFirst: " << mc.mFirst << std::endl;
std::cout << "mc.mSecond: " << mc.mSecond << std::endl;
int a;
std::cout << "a: " << a << std::endl;
return 0;
}
However, the output stays the same:
mc.mFirst: 0
mc.mSecond: 0
a: 0
Can anyone explain why?
Update 2
Ok I figured it out. GCC was probably optimizing away unused variables.
This application shows the expected behavior:
#include <iostream>
struct MyClass
{
MyClass() : mFirst() { }
MyClass(int inFirst, int inSecond) : mFirst(inFirst), mSecond(inSecond) { }
int mFirst;
int mSecond;
};
int main()
{
// Fill the stack with non-zeroes
// Use volatile to prevent GCC optimizations.
{
volatile MyClass mc(1, 2);
volatile int a = 3;
}
{
volatile MyClass mc;
volatile int a;
std::cout << "mc.mFirst: " << mc.mFirst << std::endl;
std::cout << "mc.mSecond: " << mc.mSecond << std::endl;
std::cout << "a: " << a << std::endl;
}
return 0;
}
Output:
$ g++ -o test main.cpp
$ ./test
mc.mFirst: 0
mc.mSecond: 2
a: 3
They're (EDIT: With "They" i refer to mSecond and a which are not explicitly initialized) not initialized. Your stack happens to contain 0's so that's the values you get.
8.5/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.
Otherwise, if no initializer is
specified for a nonstatic object, the
object and its subobjects, if any,
have an indeterminate initial
value; if the object or any of its
subobjects are of const-qualified
type, the program is ill-formed.
They are classified as 'undefined' meaning whatever the compiler decides or whatever was in memory at that location at the time of creation. Basically, leaving them uninitialised is leaving it up to chance and you should initialise them. The only reason for not initialising members of a struct that I can think of is if you have a very, very large array and don't want to be calling a constructor many times.