I am following a C++ training and I found out a behavior I find weird in C++ when learning about explicit keyword.
About the following snippet, it will compile and execute without any error or warning (compile with G++).
When calling Foo(5), it will automatically do an implicit conversion and actually call Foo(A(5)).
I know I can forbid this behavior by making the constructor of A explicit : explicit A(int a); but my question is :
Is there a way to make G++ warn about implicit conversion like that if I forgot to do so?
I tried g++ with -Wall -Wextra, I saw stuff on -Wconversion on SO but still build without any warnings.
Looking on the internet I found out some warnings seem to exist for C and ObjC, but not for C++...
Source: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
The snippet:
#include <iostream>
class A{
public:
A(int a)
{
m_a = a;
};
int m_a;
};
void Foo(A a)
{
std::cout << "Hello : " << a.m_a << std::endl;
}
int main(int argc, char** argv)
{
Foo(5); // No Warning ?
return 0;
}
Output
Hello : 5
Is there a way to make G++ warn about implicit casts like that if I forgot to do so?
No, the fact that you have a choosen to have a non-explicit conversion constructor says that you want implicit conversion from int to Foo allowed.
If that is not desirable, then you always have the option of making this converting ctor explicit to forbid this completely. No way to have it both ways.
Now I realize that for code protection I should make all my CTORs explicit to avoid this, which I find a bit cumberstone
You pay for what you use.
Related
Consider the code below and suppose that several years after; a colleague adds a new implementation of SetRotationSpeed(int i) in a DLL knowing that, the code of the class containing the implicit conversion has been compiled in static mode.
There will be no compilation errors except that it may change the behavior of the program, which in my opinion constitutes a potential danger to the behavior of the program.
Has the C++ standard thought about this edge effect?
In this topic, I am not particularly interested in the use of the explicit keyword but in an edge effect that we can encounter on a program developed in the form of separated DLLs by several developers who do not necessarily know each the part developed by the other one.
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
A(int n)
: m_RotationalScannerSpeed(n/2)
{
std::cout << "Scanner Rotation speed"<< m_RotationalScannerSpeed<<endl;
}
private:
int m_RotationalScannerSpeed;
};
void SetRotationSpeed(A a){
};
// Function added 2 years later by a collegue in a DLL...
/*void SetRotationSpeed(int i){
};*/
int main (int argc, char* argv[])
{
int i=5;
SetRotationSpeed(i);
}
You should usually use explicit specifier to avoid such situations. When a constructor is marked explicit, no implicit conversions can be made using that constructor. Only explicit conversions are allowed, which is usually a good thing. Clang even has a nice warning about that:
Clang-Tidy: Single-argument constructors must be marked explicit to
avoid unintentional implicit conversions
Consider the following toy program(prog.cpp):
class A {
public:
vector<int> vec;
A() noexcept {}
A(vector<int> s) : vec(s) {}
};
class B {
private:
vector<atomic<A>> a_table;
public:
B(int capacity) : a_table(capacity) {}
void update(int index) {
A newValue(vector<int>(10,1));
a_table[index].store(newValue);
}
};
int main(int argc, char** argv)
{
B b(5);
b.update(2);
return 0;
}
This when compiled normally (g++ prog.cpp -latomic), works fine. But when compiled as g++ -fsanitize=address -fno-omit-frame-pointer prog.cpp -latomic produces Double Free error when executed. A program based on similar lines as above has to be used in a multi-threaded application, where even the normal compilation is producing Double Free error. I read up Rule of Three/Five, which is generally referred to in case of Double Free, and various other documentations but nothing worked.
Also, removing noexcept specifier from the class A's default constructor produces this strange error, which also I would like to know about.
error: function ‘std::atomic<_Tp>::atomic() [with _Tp = A]’ defaulted on its first declaration with an exception-specification that differs from the implicit declaration ‘std::atomic<A>::atomic()’
atomic() noexcept = default;
^
std::atomic requires a trivially copyable type, which your A is not, because its member of type vector<int> (e.g.) is not trivially copy constructible.
GCC only detects a violation of that requirement since version 5.0.
The fact that older gcc versions compile the code does not mean that it is valid.
string foo() { return "hello"; }
int main()
{
//below should be illegal for binding a non-const (lvalue) reference to a rvalue
string& tem = foo();
//below should be the correct one as only const reference can be bind to rvalue(most important const)
const string& constTem = foo();
}
GCC is the good one to give a compile error: invalid initialization of non-const reference of type std::string& from a temporary of type std::string
VS2008 is not too bad as at least it gives a compile warning:
warning C4239: nonstandard extension used : 'initializing' :
conversion from std::string to std::string & A non-const
reference may only be bound to an lvalue
Here comes the problematic one - VS2010(SP1) comples fine WITHOUT any
error or warning, WHY ??!!
I know rvalue reference in VS2010 can be used to bind with rvalue but I am NOT using &&, instead in the demo code, I was just using non-const lvalue reference !
Can somone help me explain the behavior of VS2010 here? Is it a bug !?
Thanks
That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.
The compiler will issue an error with Disable Language Extensions turned on, and a warning at /W4. However, removing this code will break previously compiling code, and Microsoft is very reluctant to do that. This is also why they won't fix their SFINAE support.
Several years and many versions of Visual Studio later, we still have this "extension" causing surprises and headaches. Sigh...
The fix is to simply turn warning C4239 into an error. This will prevent MSVC from compiling code that attempts to bind a non-const lvalue reference to a temporary, and give you a nice clear compiler error. Simply add /we4239 to your compiler definitions or cl command line arguments.
In Visual Studio:
Project Properties > C/C++ > All Options > Treat Specific Warnings As Errors > add 4239, and make sure to separate any other numbers with a semicolon.
In CMake:
if(MSVC)
add_definitions("/we4239")
endif()
This seems to work far better than disabling all language extensions with /Za, which officially not recommend. On my large code base, adding /Za caused over 1500 compiler errors from Microsofts's own winnt.h header.
There is a much nastier variant of this problem:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo& f;
public:
Bar(Foo& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}
So: to what does b.f point during the call to b.F()? The above example, compiled with VS2013 default Debug settings, runs without crashing and prints 3, but I'd suspect that any much more complex example will lead to stack corruption. If it doesn't and the compiler is doing something 'clever' to make it work, then I guess what it is really doing is this:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo f;
public:
Bar(Foo&& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}
I'm curious to know what happens when compiling code with a dynamic cast whith RTTI disabled
(either with -fno-rttion GCC or with /GR- on visual studio). Does the compiler "falls back" to static_cast ? Since (at least on VS) it does only issue a warning, what will the compiled code do ?
More specifically, what bad things could happen if I compile without RTTI a code where I'm sure that there are no error possible with dynamic_cast (i.e. where dynamic_cast could be safely replaced by a static_cast) like this one :
class A{ /*...*/ } ;
class B : public A {
int foo() { return 42 ;}
} ;
//...
A * myA = new B() ;
int bar = (dynamic_cast<B*>(myA))->foo() ;
Reading the standard, in 5.2.7/6 we find that unless the target is an unambiguous base of the source, source must be a polymorphic type. Then in 10.3/1
Virtual functions support dynamic binding and objectoriented
programming. A class that declares or inherits a virtual function is
called a polymorphic class.
In other words the standard doesn't seem to say anything about your question. In this case, the standard doesn't allow for a compiler to turn off RTTI so for each compiler you need to check its documentation to see what would happen. Based on this reading, I think this is a compiler question, not a C++ language question as the tag indicates.
Alternately you can avoid the problem completely by just using static_cast when you know it's sufficient.
In MSVC, if your code is not compiled with RTTI enabled, a __non_rtti_object exception will be thrown, if the cast cannot be performed without a run-time check.
The easiest way to find out is to try it.
What you will find is that some of your dynamic casts will be flagged as illegal. Some won't. For example, the conversion is known at compile time when you use dynamic cast to upcast to an unambiguous base class.
Addendum
Re "Since (at least on VS) it does only issue a warning ..."
Ignore warnings at your peril. The best thing to do is to ensure that your code compiles without warnings, with warning levels set very high (and possibly converted to errors). Second best is to look at each and every warning you get and ensure that nothing untoward happens. In this case, something untoward will happen. You really should not care how that untoward event is implemented. What you should care about is getting rid of it.
Just try it:
#include <iostream>
#include <typeinfo>
#include <typeindex>
#include <memory>
#include <vector>
#include <array>
#include <string>
class Base {
public:
virtual ~Base() {
}
};
class A: public Base {
};
class B: public Base {
};
using namespace std;
int main() {
A *a = new A;
auto *ptr = dynamic_cast<B*>(a);
if (!ptr)
std::cout << "failed to cast" << std::endl;
return 0;
}
Without -fno-rtti, the program compiles and the output is:
failed to cast
With -fno-rtti, the program failed to compile:
main.cpp:25:35: error: ‘dynamic_cast’ not permitted with -fno-rtti
auto* ptr = dynamic_cast<B*>(a);
^
You can also test this online here: https://onlinegdb.com/pYTQu2ne2
string foo() { return "hello"; }
int main()
{
//below should be illegal for binding a non-const (lvalue) reference to a rvalue
string& tem = foo();
//below should be the correct one as only const reference can be bind to rvalue(most important const)
const string& constTem = foo();
}
GCC is the good one to give a compile error: invalid initialization of non-const reference of type std::string& from a temporary of type std::string
VS2008 is not too bad as at least it gives a compile warning:
warning C4239: nonstandard extension used : 'initializing' :
conversion from std::string to std::string & A non-const
reference may only be bound to an lvalue
Here comes the problematic one - VS2010(SP1) comples fine WITHOUT any
error or warning, WHY ??!!
I know rvalue reference in VS2010 can be used to bind with rvalue but I am NOT using &&, instead in the demo code, I was just using non-const lvalue reference !
Can somone help me explain the behavior of VS2010 here? Is it a bug !?
Thanks
That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.
The compiler will issue an error with Disable Language Extensions turned on, and a warning at /W4. However, removing this code will break previously compiling code, and Microsoft is very reluctant to do that. This is also why they won't fix their SFINAE support.
Several years and many versions of Visual Studio later, we still have this "extension" causing surprises and headaches. Sigh...
The fix is to simply turn warning C4239 into an error. This will prevent MSVC from compiling code that attempts to bind a non-const lvalue reference to a temporary, and give you a nice clear compiler error. Simply add /we4239 to your compiler definitions or cl command line arguments.
In Visual Studio:
Project Properties > C/C++ > All Options > Treat Specific Warnings As Errors > add 4239, and make sure to separate any other numbers with a semicolon.
In CMake:
if(MSVC)
add_definitions("/we4239")
endif()
This seems to work far better than disabling all language extensions with /Za, which officially not recommend. On my large code base, adding /Za caused over 1500 compiler errors from Microsofts's own winnt.h header.
There is a much nastier variant of this problem:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo& f;
public:
Bar(Foo& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}
So: to what does b.f point during the call to b.F()? The above example, compiled with VS2013 default Debug settings, runs without crashing and prints 3, but I'd suspect that any much more complex example will lead to stack corruption. If it doesn't and the compiler is doing something 'clever' to make it work, then I guess what it is really doing is this:
class Foo {
int _val;
public:
Foo(int v) : _val(v) {}
void F() { std::cout << _val << std::endl; }
};
class Bar {
Foo f;
public:
Bar(Foo&& f) : f(f) {}
void F() { f.F(); }
};
int main() {
Bar b(Foo(3));
b.F();
}