Default argument initializer: Local variable error in clang but not msvc - c++

In Visual Studio 2022 the code below compiles fine with the MSVC compiler selected but gives the indicated error with the clang compiler selected. My understanding is that a local variable is one whose defining declaration is inside a function. Could someone please clarify?
"...default argument references local variable 'bar' of enclosing function"
int bar = 5, x = bar;
int main()
{
extern int bar;
int Test(int foo = bar);
}

found intresting behaviour:
#include <iostream>
using namespace std;
int bar = 5, x = 6;
int Test2(int i = bar)
{
cout << i << endl;
return i;
}
int main()
{
{
int Test2(int foo=x);
Test2(); // prints 6
}
Test2(); // prints 5
}
The output is
6
5
I am using UCRT64 from MSYS to compile.
Using default params, is dangerous. And, somebody may able to override default function param value by redefining function in its scope.

Related

C++ does not recognize string as keyword

I tried the following code:
#include <iostream>
using namespace std;
int main()
{
char string[4]='xyz';
return 0;
}
Since string is a keyword the compiler should give error but it runs fine. Can anyone explain why it compiles successfully.
string is not a keyword.
It's the name of a type declared in the standard library.
When you give it a name, you're doing something called shadowing. This is more clear in the following example:
{
int x = 0;
{
int x = 5;
std::cout << x << std::endl;
}
std::cout << x << std::endl;
}
What gets printed?
Well, 5 first then 0.
This is because the x in the second scope overrides the x from the first. It "shadows" the first declaration.
This works with typenames as well:
struct MyStruct {
int x;
};
...
{
...
int MyStruct = 10;
...
}
Here, MyStruct gets overridden within that scope.
That same thing happens in your example with std::string

Passing global variables as const reference

The following code compiles and works. The value displayed of both a and n are 4.
#include <iostream>
using namespace std;
int a = 2;
void foo(int const&n)
{
a = n*2;
cout<<"a = "<<a<<" n = "<<n<<endl;
}
int main()
{
foo(a);
}
OUTPUT: a = 4 n = 4
Why does the compiler not complain about n being a const reference? For example, the following code fails to compile.
#include <iostream>
using namespace std;
int a = 2;
void foo(int const&a)
{
a = a*2;
cout<<"a = "<<a<<endl;
}
int main()
{
foo(a);
}
OUTPUT: In function 'void foo(const int&)':
10:7: error: assignment of read-only reference 'a'
How are the two cases different ?
In the first case you're assigning to a global variable a. n changes because it's a reference to the mutable global variable. Changing a is allowed, but changing n directly is forbidden.
In the second case you're trying to re-assign to the const argument a. This is forbidden as a is const.
What you've done is shadow global variable a with a local variable. In the second example, within foo the global variable named a does not exist, instead there's an argument that occupies that name.

I compiled this seemingly incorrect code, but I don’t understand why

I am learning C++ on a linux machine. I just tried “int i();” to declare a function but I forgot to define it. But to my surprise, this code can be compiled and output 1. I feel very confused. I tried “int I{};”, it still compiled with no errors. Please help to explain. Thanks in advance.
//test1.cpp
#include <iostream>
int main(void)
{
int i{};
std::cout << i << std::endl;
return 0;
}
g++ test1.cpp
./a.out
Output is: 0
//test2.cpp
#include <iostream>
int main(void)
{
int i();
std::cout << i << std::endl;
return 0;
}
g++ test2.cpp
./a.out
Output is : 1
In your first example, you define a variable named i, and value-initialise it, which for int means zero-initialisation.
int i{}; // defines i, initialised to zero
In your second example, you declare a function named i, which takes no parameters, and return int:
int i(); // declares a function
When you print this:
std::cout << i << std::endl;
i first get converted to bool (i decays to a function non-nullptr pointer, then it becomes true), and then printed as an integer, that's why you get 1. The compiler can make this conversion without the definition of i (as the result is always true), that's why you got no linker error.
If your intent was to call this function, and print the result, you'll need to use i():
std::cout << i() << std::endl;
This, of course, needs i's definition.
In your code:
//test1.cpp
#include <iostream>
int main(void)
{
int i{};
std::cout << i << std::endl;
return 0;
}
You are not actually declaring a function without defining it. The line of code int i{}; within the main() function here is a variable of type int named i and you are using a brace initializer list to initialize the variable i with out any values and in most cases could be 0 but can vary by compiler.
//test2.cpp
#include <iostream>
int main(void)
{
int i();
std::cout << i << std::endl;
return 0;
}
In this situation it is basically the same thing. You are within main() and by the rules of the language "you can not declare-define a function within a function", so this results in a declaration - definition of a variable. The only difference here is you are not using a brace initializer list here you are using it's ctor constructor called value initialization. Again you are not passing any values to it and in your case it's assigning an arbitrary value of 1.
Now if your code looked like this:
#include <iostream>
int i();
int main() {
std::cout << i() << '\n';
return 0;
}
This would fail to compile because the function i is declared but not defined. However if you did this:
#include <iostream>
// The text in quotes is not meant to be a string literal. It
// is the message of the text that represents any integer X.
int i() { return /*"some int value"*/ 1; }
int main() {
std::cout << i() << '\n';
return 0;
}
This would compile and run perfectly fine because the function i is both declared and defined.

Calling a function via the main using a class

I'm trying to add 2 to a class variable using a function, but it gives me this undefined reference to addTwo(int) even though I already have it declared.
#include <stdio.h>
#include <iostream>
using namespace std;
class Test {
public:
int addTwo(int test);
int test = 1;
};
int addTwo(int test);
int main() {
Test test;
cout << test.test << "\n";
addTwo(test.test);
cout << test.test;
}
int Test::addTwo(int test) {
test = test + 2;
return test;
}
The defined member function int Test::addTwo(int test) do differ from the declared global function int addTwo(int test);, which the compiler searches for.
To eliminate the error, define the global function or change the call of the global function to call of the member function.
In order to "add 2 to a class variable using a function", you should stop shadowing the member variable by the argument. (You can use this->test for using member variable, but this won't be needed in this case)
Try this:
#include <iostream>
using namespace std;
class Test {
public:
int addTwo();
int test = 1;
};
int main() {
Test test;
cout << test.test << "\n";
test.addTwo();
cout << test.test;
}
int Test::addTwo() {
test = test + 2;
return test;
}
Since it is a member function of the instance test you have to call it as
test.addTwo(test.test);
Instead, you're calling it as
addTwo(test.test);
and it doesn't know what that function is. As far as the compiler is concerned, addTest(int) doesn't exist because you haven't defined it outside of the class definition.

Define a new type in a function return value

I was surprised to discover that the following code compiles, runs, AND produces the expected output under MSVC:
#include <iostream>
using namespace std;
struct Foo{
int _x;
Foo(int x): _x(x) {}
} //Note: no semi-colon after class definition.
//Makes this behave as a return type for the following function:
Foo_factory(int x)
{return Foo(x);}
int main (int argc, char* argv[])
{
Foo foo = Foo_factory(42);
cout << foo._x << endl; //Prints "42"
return 0;
}
I was less surprised to see MinGW fail to compile with the error "new types may not be defined in a return type". Is this just another Microsoft exception to the standard, or is this legal C++?
In N3797 (C++14) and N3485 (C++11), §8.3.5 [dcl.fct]/9 explicitly starts with:
Types shall not be defined in return or parameter types.
Thus, your code is invalid and GCC is correct to diagnose it.