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.
Related
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.
I encountered this problem while initializing a member array variable (c-style). Interestingly converting the member into a std::array<> solves the problem.
See below:
struct A {
A(int aa) : a(aa) {}
A(const A &a) = delete;
A &operator=(const A &a) = delete;
private:
int a;
std::vector<int> v;
};
struct B1 {
B1() : a1{{{1}, {2}}} {} // -> compiles OK (gcc 9.2)
private:
std::array<A, 2> a1;
};
struct B2 {
B2()
: a2{{1}, {2}} // -> error: use of deleted function 'A::A(const A&)' (gcc 9.2)
{}
private:
A a2[2];
};
Demo
My question is why is this difference in (compiler) behavior? I was assuming that functionality-wise they are pretty much the same (I understand std::array<> is more of an aggregate instead of a pure container, like vector - I might be wrong though).
Additional observation:
The compiler allows c-style array if I remove the vector<> member in A
GCC 9.5 does not raise any issues. Does it mean it's a GCC bug (I couldn't find anything on the release notes)?
Update:
It is a GCC bug:
Brace initialization of array sometimes fails if no copy constructor (reported in 4.9 and resolved in 9.4)
Does it mean it's a GCC bug
Yes, this seems to be a gcc bug that has been fixed in gcc 9.4. Demo.
A bug for the same has been submitted as:
GCC rejects valid program involving initialization of array in member initializer list
Suppose you have a class called Product, defined like this:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs);
Product(const Product &rhs);
~Product();
private:
const char *m_name;
int m_i;
};
and you initialize a variable like this:
auto p = Product{"abc",123};
I thought that the standard dictated that a compiler must logically do the following:
construct a temporary Product
move-construct p (using the temporary Product)
But that the compiler was allowed to optimize this so that p is directly constructed.
I verified this (Visual Studio 2013) and indeed, the compiler optimizes this, even if we have our own custom (non-default) move-constructor. This is fine.
However, if I explicitly delete the copy- and move-constructor, like this:
class Product
{
public:
Product(const char *name, int i);
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
~Product();
private:
const char *m_name;
int m_i;
};
The auto+brace initialization still compiles. I though the compiler had to prevent this because there is no copy- or move- allowed.
Strange enough, if I make the deleted copy- and move-constructor private, like this:
class Product
{
public:
Product(const char *name, int i);
~Product();
private:
Product(Product &&rhs) = delete;
Product(const Product &rhs) = delete;
const char *m_name;
int m_i;
};
Then the auto+brace initialization doesn't compiler anymore.
error C2248: 'Product::Product' : cannot access private member declared in class 'Product'
Is this expected behavior?
Is this a bug in Visual Studio 2013 (Update 3)?
Note:
I tried compiling this on ideone and there it indeed refuses to compile the initialization when the copy- and move-constructors are deleted (and public). So I think this is a Visual Studio bug.
the standard is very clear as you mentioned before, indicating that this is a bug in the cl-compiler. You never can be sure, though if one compiler is saying something and all others disagree, I expect that this would be one of the many non-standard-compliant implementations of the MSVC compiler.
The interpretation of clang version 3.7 (svn-build):
t.cpp:19:7:{19:11-19:30}: error: call to deleted constructor of 'Product'
[Semantic Issue]
auto p = Product{"abc", 123};
^ ~~~~~~~~~~~~~~~~~~~
t.cpp:8:2: note: 'Product' has been explicitly marked deleted here
[Semantic Issue]
Product(Product &&rhs) = delete;
^
1 error generated.
make: *** [t.o] Error 1
The interpretation of gcc 4.8:
t.cpp: In function ‘int main()’:
t.cpp:19:29: error: use of deleted function ‘Product::Product(Product&&)’
auto p = Product{"abc", 123};
^
t.cpp:8:2: error: declared here
Product(Product &&rhs) = delete;
^
make: *** [build/gcc/t.o] Error 1
Keep also in mind that the Explicitly Defaulted and Deleted Functions are new since MSVC 2013 and the implementation of it is not yet complete. Like it does not yet understand =default for move constructors.
My guess would be that MSVC 2013 does not check for the move constructor or simply falls back to the copy constructor.
It might be interesting to check MSVC 2015, since it appears to have a (more) complete implementation of these constructions.
JVApen
In your line
auto p = Product{"abc",123};
the equal sign is not the denoting the assignment operator, but is just the syntax for an initializer. Hence, the compiler is not optimizing anything, but just doing the initialization.
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();
}
Why is the output of the following program just int3 and not int3&4?
#include <iostream>
class B
{
public:
explicit B(int i) { std::cout<<"int"<<i; }
B(const B& rhs, int i = 0) { std::cout<<"&"<<i; }
};
int main(int, char**)
{
B b(B(3), 4);
}
Command: clang++ test.cpp -O0
Compiler: Apple clang version 3.0 (tags/Apple/clang-211.12) (based on LLVM 3.0svn)
Looks like you might have found a compiler quirk :)
If you change your compiler version to anything that's not LLVM 3.0, the output is int3&4.
This prints int3&4 on LLVm 3.0, so it seems to be related to the fact that B(3) is a temporary object:
class B
{
public:
explicit B(int i)
{
std::cout<<"int"<<i;
}
B(const B& rhs, int i = 0)
{
std::cout<<"&"<<i;
}
};
int main(int, char**)
{
B a(3);
B b(a, 4);
}
This was a bug in clang, which has since been fixed. Copy-elision was incorrectly being applied to the constructor call, because clang wasn't checking how many arguments were provided before concluding that it was a copy construction.
The fix will be in the upcoming clang 3.1 release.
Most likely, RVO and NRVO ate your code. These special conditions allow the compiler to silently eliminate object copies that would otherwise be enforced by the language. Since no copy was ever made as a result, then the code never prints the statement in the copy constructor.