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.
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
What I want to do can be summarized into the following code:
struct A{};
struct B{
A& a;
B(A& a) noexcept : a(a){}
int operator()(int) {}
};
int main(){
A a;
B(a)(2);
}
And my compiler (g++ 6) rejected the code complaining that a shadows a parameter. However, if I try to explicitly call operator(), it works as expected.
It seems that g++ will ignore the parentheses and see the statement as a declaration.
Is this the specified or expected behavior?
This is one of those icky parsing rules which catches you every now and again. As you suggest, B(a)(2); is actually equivalent to B a(2);, so your code tries to initialize a B with an int.
To fix this, you can use C++11's uniform initialization:
B{a}(2);
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.
Why there is default move constructor or assignment operator not created for derived classes? To demonstrate what I mean; having this setup code:
#include <utility>
struct A
{
A () { }
A (A&&) { throw 0; }
A& operator= (A&&) { throw 0; }
};
struct B : A
{ };
either of the following lines throws:
A x (std::move (A ());
A x; x = A ();
but neither of the following does:
B x (std::move (B ());
B x; x = B ();
In case it matters, I tested with GCC 4.4.
EDIT: Later test with GCC 4.5 showed the same behavior.
Reading through 12.8 in the 0x FCD (12.8/17 in particular for the move ctor), this appears to be a GCC bug. I see the same thing happening in 4.5 as you do in 4.4.
I may be missing a corner case on deleted functions, or something similar, but I don't see any indication of that yet.