In a C++ Class, I 've overloaded operator-(). When I compile with -O0, it behaves as expected but when I compile with at least -O1, the result of calling operator-() is wrong despite no error is generated.
I'm not a C++ guru but nothing seems weird to me in the code snippet below.
Unlike other cases I encountered on this site, I have no inline asm, I'm not calling a library, etc. Everything is self contained in the code.
Here are the version of gcc I used :
g++ (GCC) 4.9.3
Copyright © 2015 Free Software Foundation, Inc.
Ce logiciel est libre; voir les sources pour les conditions de copie.
Il n'y a PAS GARANTIE; ni implicite pour le MARCHANDAGE ou pour un BUT PARTICULIER.
g++ (GCC) 5.2.0
Copyright © 2015 Free Software Foundation, Inc.
Ce logiciel est libre; voir les sources pour les conditions de copie.
Il n'y a PAS GARANTIE; ni implicite pour le MARCHANDAGE ou pour un BUT PARTICULIER.
Compilation commands :
OK : g++ --std=c++11 -O0 -o test test.cpp
KO : g++ --std=c++11 -O1 -o test test.cpp
I get the expected warning :
test.cpp: In member function ‘A& A::operator-(const A&)’:
test.cpp:23:5: attention : reference to local variable ‘tmp’ returned
[-Wreturn-local-addr]
A tmp(*this);
^
Adding -Wno-return-local-addr changes nothing since it's just removing the warning
Here is the code snippet which reproduces the problem :
#include <iostream>
using namespace std;
class A {
public:
double val;
A(int _val) : val(_val) {}
A() : val(0.0) {}
A(const A&) = default;
A(A&&) = default;
A& operator=(const A&) = default;
A& operator=(A&&) = default;
A& operator-(const A& other) {
A tmp(*this);
tmp.val -= other.val;
return tmp;
}
};
int main() {
A a(3);
A b(2);
A c = b - a;
cout << c.val << endl;
return 0;
}
If I add an operator-=()
A& operator-=(const A& other) {
this->val -= other.val;
return *this;
}
and change in main :
A c = b - a;
with
A c(b);
c -= a;
I works perfectly whatever the -Ox option.
I suspect that returning a reference to a local variable from operator-() is the source of the problem (despite the RVO feature of c++11 ?).
What I don't get is why the level of optimization has such an effect ?
After all, is there something wrong with mys code ?
Your operator-() is returning a reference to a local variable. That causes the caller to exhibit undefined behaviour if it uses that reference.
Conventionally, operator-() returns an object by value, not by reference. This makes sense, as c = a - b typically leaves a and b unchanged, and gives c the difference (however that is defined) between them.
Related
1.FORTRAN source (main.for)
integer function mysum(a, b)
!DEC$ATTRIBUTES DLLEXPORT,STDCALL :: mysum
!DEC$ATTRIBUTES VALUE :: a, b
integer a,b
mysum = a + b
return
end function mysum
make dll
gfortran main.for -shared -o fordll.dll
call dll
#include <stdio.h>
#include <iostream>
#include <windows.h>
using namespace std;
typedef int(_stdcall * MYSUM)(int a, int b);
int main()
{
int a=10,b=20;
HINSTANCE hLibrary = LoadLibrary("fordll.dll");
if (hLibrary == NULL)
{
cout << "can't find the dll file" << endl;
return -1;
}
MYSUM fact = (MYSUM)GetProcAddress(hLibrary, "mysum");
if (fact == NULL)
{
cout << "can't find the function file." << endl;
return -2;
}
try
{
cout << fact(a,b);
}
catch(...)
{ }
FreeLibrary(hLibrary);
return 0;
}
ERROR
Exception Access Violation reading 0x0000000A
why? if the fortran source file is comppiled by Compad Visual fortran or Inter fortran, it works well. However, it doesn't work with gcc or gfortran. What's wrong?
You are using special directives to alter the calling conventions
!DEC$ATTRIBUTES DLLEXPORT,STDCALL :: mysum
!DEC$ATTRIBUTES VALUE :: a, b
However, these are only valid for the DEC compiler sand its descendant Intel Fortran.
GCC use !GCC$ directives instead. Use them, they are pretty much the same as the DEC ones. See https://gcc.gnu.org/onlinedocs/gfortran/ATTRIBUTES-directive.html#ATTRIBUTES-directive Just change coppy and paste the DEC directives and change DEC to GCC.
Alternatively, change the code to pass-by-reference and ditch the VALUE attribute. The STDCALL attribute is relevant for 32-bit Windows only.
In modern Fortran it is much better to use
integer function mysum(a, b) bind(C,name="mysum")
integer, value :: a, b
(ignoring the stdcall issue, which can be just deleted in the C++ code).
This question already has an answer here:
Why C++ async run sequentially without future?
(1 answer)
Closed 2 years ago.
Description of the problem
std::async seems to block even with std::launch::async flag:
#include <iostream>
#include <future>
#include <chrono>
int main(void)
{
using namespace std::chrono_literals;
auto f = [](const char* s)
{
std::cout << s;
std::this_thread::sleep_for(2s);
std::cout << s;
};
std::cout << "start\n";
(void)std::async(std::launch::async, f, "1\n");
std::cout << "in between\n";
(void)std::async(std::launch::async, f, "2\n");
std::cout << "end\n";
return 0;
}
output shows that the execution is serialized. Even with std::launch::async flag.
start
1
1
in between
2
2
end
But if I use returned std::future, it suddenly starts to not block!
The only change I made is removing (void) and adding auto r1 = instead:
#include <iostream>
#include <future>
#include <chrono>
int main(void)
{
using namespace std::chrono_literals;
auto f = [](const char* s)
{
std::cout << s;
std::this_thread::sleep_for(2s);
std::cout << s;
};
std::cout << "start\n";
auto r1 = std::async(std::launch::async, f, "1\n");
std::cout << "in between\n";
auto r2 = std::async(std::launch::async, f, "2\n");
std::cout << "end\n";
return 0;
}
And, the result is quite different. It definitely shows that the execution is in parallel.
start
in between
1
end
2
1
2
I used gcc for CentOS devtoolset-7.
gcc (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
My Makefile is:
.PHONY: all clean
all: foo
SRCS := $(shell find . -name '*.cpp')
OBJS := $(SRCS:.cpp=.o)
foo: $(OBJS)
gcc -o $# $^ -lstdc++ -pthread
%.o: %.cpp
gcc -std=c++17 -c -g -Wall -O0 -pthread -o $# $<
clean:
rm -rf foo *.o
Question
Is this behaviour in the specification?
Or is it a gcc implementation bug?
Why does this happen?
Can someone explain this to me, please?
The std::future destructor will block if it’s a future from std::async and is the last reference to the shared state. I believe what you’re seeing here is
the call to async returns a future, but
that future is not being captured, so
the destructor for that future fires, which
blocks, causing the tasks to be done in serial.
Explicitly capturing the return value causes the two destructors to fire only at the end of the function, which leaves both tasks running until they’re done.
I'm porting some code from C11 to C++. The project compiles fine in gcc and g++ but clang refuses to compile it. The offending lines are shown below:
static atomic_int sem = {0};
src/testdir.c:27:25: error: illegal initializer type 'atomic_int'
(aka '_Atomic(int)')
and
struct container {
volatile atomic_ullong workerThreadCount;
struct cfgoptions *config;
repaircmd_t *cmd;
};
Container container = {{0}, s, NULL};
src/testdir.c:334:25: error: illegal initializer type 'volatile atomic_ullong'
(aka 'volatile _Atomic(unsigned long long)')
Clang:
clang version 3.7.0 (tags/RELEASE_370/final)
gcc:
gcc (GCC) 5.3.1 20160406 (Red Hat 5.3.1-6)
Operating system:
Fedora 23
Test code:
https://gist.github.com/clockley/eb42964003a2e4fe6de97d5b192d61d3
P.S i = {0} or i(0) are the only valid initializers in C++ as atomic ints are not primitive types of the two only the former is valid C.
I believe this is a bug in clang.
Here's a simple test case, with the result of compiling it with gcc and clang:
$ cat c.c
static _Atomic int x = ( 42 );
static _Atomic int y = { 42 };
$ gcc-6.1.0 -std=c11 -c c.c
$ clang-3.7 -std=c11 -c c.c
c.c:2:24: error: illegal initializer type '_Atomic(int)'
static _Atomic int y = { 42 };
^
1 error generated.
$
C explicitly permits the initializer for a scalar object to be enclosed in braces (N1570 6.7.9p11). I see nothing that forbids such an initializer for an atomic object.
Atomics are an optional feature in C11, but both gcc and clang support it (neither predefines __STDC_NO_ATOMICS__).
As a workaround, I suggest just dropping the braces. This:
static _Atomic int z = 42;
is valid and accepted by both compilers.
If you're need the code to compile both as C and as C++, then you might want to reconsider that requirement. But if it's really necessary, you can use the __cplusplus predefined macro to distinguish between C and C++ compilers:
static _Atomic int foo =
#ifdef __cplusplus
{ 42 };
#else
42;
#endif
or play some other tricks with macros.
(I'll note that C11's <stdatomic.h> header defines a macro ATOMIC_VAR_INIT that's intended to be used to initialize atomic objects, with an example:
atomic_int guide = ATOMIC_VAR_INIT(42);
It appears to be needed for atomic objects with automatic storage duration, which doesn't apply here.)
I must disagree strongly with Mad Physicist. The braces {} are used to initialize an aggregate. A single int is not an aggregate. You may put the initializer in () if you like, but {} are verboten. It would be an aggregate if you dimensioned it out to one entry, as in:
static atomic_int sem[ 1 ] = { 0 };
or even
static atomic_int sem[] = { 0 };
if you're too lazy to count one integer initializer!
Here's a short example that reproduces this “no viable conversion” with lemon for clang but valid for g++ difference in compiler behavior.
#include <iostream>
struct A {
int i;
};
#ifndef UNSCREW_CLANG
using cast_type = const A;
#else
using cast_type = A;
#endif
struct B {
operator cast_type () const {
return A{i};
}
int i;
};
int main () {
A a{0};
B b{1};
#ifndef CLANG_WORKAROUND
a = b;
#else
a = b.operator cast_type ();
#endif
std::cout << a.i << std::endl;
return EXIT_SUCCESS;
}
live at godbolt's
g++ (4.9, 5.2) compiles that silently; whereas clang++ (3.5, 3.7) compiles it
if
using cast_type = A;
or
using cast_type = const A;
// [...]
a = b.operator cast_type ();
are used,
but not with the defaulted
using cast_type = const A;
// [...]
a = b;
In that case clang++ (3.5) blames a = b:
testling.c++:25:9: error: no viable conversion from 'B' to 'A'
a = b;
^
testling.c++:3:8: note: candidate constructor (the implicit copy constructor)
not viable:
no known conversion from 'B' to 'const A &' for 1st argument
struct A {
^
testling.c++:3:8: note: candidate constructor (the implicit move constructor)
not viable:
no known conversion from 'B' to 'A &&' for 1st argument
struct A {
^
testling.c++:14:5: note: candidate function
operator cast_type () const {
^
testling.c++:3:8: note: passing argument to parameter here
struct A {
With reference to the 2011¹ standard: Is clang++ right about rejecting the defaulted code or is g++ right about accepting it?
Nota bene: This is not a question about whether that const qualifier on the cast_type makes sense. This is about which compiler works standard-compliant and only about that.
¹ 2014 should not make a difference here.
EDIT:
Please refrain from re-tagging this with the generic c++ tag.
I'd first like to know which behavior complies to the 2011 standard, and keep the committees' dedication not to break existing (< 2011) code out of ansatz for now.
So it looks like this is covered by this clang bug report rvalue overload hides the const lvalue one? which has the following example:
struct A{};
struct B{operator const A()const;};
void f(A const&);
#ifdef ERR
void f(A&&);
#endif
int main(){
B a;
f(a);
}
which fails with the same error as the OP's code. Richard Smith toward the end says:
Update: we're correct to choose 'f(A&&)', but we're wrong to reject
the initialization of the parameter. Further reduced:
struct A {};
struct B { operator const A(); } b;
A &&a = b;
Here, [dcl.init.ref]p5 bullet 2 bullet 1 bullet 2 does not apply,
because [over.match.ref]p1 finds no candidate conversion functions,
because "A" is not reference-compatible with "const A". So we fall
into [dcl.init.ref]p5 bullet 2 bullet 2, and copy-initialize a
temporary of type A from 'b', and bind the reference to that. I'm not
sure where in that process we go wrong.
but then comes back with another comment due to a defect report 1604:
DR1604 changed the rules so that
A &&a = b;
is now ill-formed. So we're now correct to reject the initialization.
But this is still a terrible answer; I've prodded CWG again. We should
probably discard f(A&&) during overload resolution.
So it seems like clang is technically doing the right thing based on the standard language today but it may change since there seems to be disagreement at least from the clang team that this is the correct outcome. So presumably this will result in a defect report and we will have to wait till it is resolved before we can come to a final conclusion.
Update
Looks like defect report 2077 was filed based on this issue.
Edit: This is indeed caused by a bug in Visual Studio - and it has already been fixed. The issue is not reproducible after applying Update 2 to Visual Studio (release candidate available here). I apologize; I thought I was up to date with my patches.
I can't for the life of me figure out why I get a seg fault when I run the following code in Visual Studio 2013:
#include <initializer_list>
#include <memory>
struct Base
{
virtual int GetValue() { return 0; }
};
struct Derived1 : public Base
{
int GetValue() override { return 1; }
};
struct Derived2 : public Base
{
int GetValue() override { return 2; }
};
int main()
{
std::initializer_list< std::shared_ptr<Base> > foo
{
std::make_shared<Derived1>(),
std::make_shared<Derived2>()
};
auto iter = std::begin(foo);
(*iter)->GetValue(); // access violation
return 0;
}
I was expecting the initializer_list to take ownership of the created shared_ptrs, keeping them in scope until the end of main.
Oddly enough, if I try to access the second item in the list, I get the expected behavior. For example:
auto iter = std::begin(foo) + 1;
(*iter)->GetValue(); // returns 2
Considering these things, I'm guessing this may be a bug in the compiler - but I wanted to make sure I wasn't overlooking some explanation for why this behavior might be expected (e.g., maybe in how rvalues are handled in initializer_lists).
Is this behavior reproducible in other compilers, or can someone explain what might be happening?
See the original answer for analysis of object lifetimes of the code in the question. This one isolates the bug.
I made a minimal reproduction. It's more code, but a lot less library code involved. And easier to trace.
#include <initializer_list>
template<size_t N>
struct X
{
int i = N;
typedef X<N> self;
virtual int GetValue() { return 0; }
X() { std::cerr << "X<" << N << ">() default ctor" << std::endl; }
X(const self& right) : i(right.i) { std::cerr << "X<" << N << ">(const X<" << N << "> &) copy-ctor" << std::endl; }
X(self&& right) : i(right.i) { std::cerr << "X<" << N << ">(X<" << N << ">&& ) moving copy-ctor" << std::endl; }
template<size_t M>
X(const X<M>& right) : i(right.i) { std::cerr << "X<" << N << ">(const X<" << M << "> &) conversion-ctor" << std::endl; }
template<size_t M>
X(X<M>&& right) : i(right.i) { std::cerr << "X<" << N << ">(X<" << M << ">&& ) moving conversion-ctor" << std::endl; }
~X() { std::cerr << "~X<" << N << ">(), i = " << i << std::endl; }
};
template<size_t N>
X<N> make_X() { return X<N>{}; }
#include <iostream>
int main()
{
std::initializer_list< X<0> > foo
{
make_X<1>(),
make_X<2>(),
make_X<3>(),
make_X<4>(),
};
std::cerr << "Reached end of main" << std::endl;
return 0;
}
The output is BAD on both x64:
C:\Code\SO22924358>cl /EHsc minimal.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
minimal.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:minimal.exe
minimal.obj
C:\Code\SO22924358>minimal
X<1>() default ctor
X<0>(X<1>&& ) moving conversion-ctor
X<2>() default ctor
X<0>(X<2>&& ) moving conversion-ctor
X<3>() default ctor
X<0>(X<3>&& ) moving conversion-ctor
X<4>() default ctor
X<0>(X<4>&& ) moving conversion-ctor
~X<0>(), i = 2
~X<2>(), i = 2
~X<0>(), i = 1
~X<1>(), i = 1
Reached end of main
~X<0>(), i = 4
~X<0>(), i = 3
~X<0>(), i = 2
~X<0>(), i = 1
and x86:
C:\Code\SO22924358>cl /EHsc minimal.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
minimal.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:minimal.exe
minimal.obj
C:\Code\SO22924358>minimal
X<1>() default ctor
X<0>(X<1>&& ) moving conversion-ctor
X<2>() default ctor
X<0>(X<2>&& ) moving conversion-ctor
X<3>() default ctor
X<0>(X<3>&& ) moving conversion-ctor
X<4>() default ctor
X<0>(X<4>&& ) moving conversion-ctor
~X<0>(), i = 2
~X<2>(), i = 2
~X<0>(), i = 1
~X<1>(), i = 1
Reached end of main
~X<0>(), i = 4
~X<0>(), i = 3
~X<0>(), i = 2
~X<0>(), i = 1
Definitely a compiler bug, and a pretty severe one. If you file a report on Connect I and many others will be happy to upvote.
The shared_ptr objects returned from make_shared are temporaries. They will be destroyed at the end of the full-expression, after being used to initialize shared_ptr<Base> instances.
But ownership of the user objects (the Derived1 and Derived2) should be shared (or "transferred" if you like) to the shared_ptr instances in the list. Those user objects should live until the end of main.
I just ran the code from your question using Visual Studio 2013 and got no access violation. Oddly, when I trace to main() and ~Base(), I get the following output:
C:\Code\SO22924358>cl /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
main.obj
C:\Code\SO22924358>main
~Base()
Reached end of main
~Base()
That does look wrong.
And if I do something with the return value of GetValue(), it is wrong (0 instead of 1) and I get the access violation. It occurs after all tracing output, however. And it seems somewhat intermittent.
C:\Code\SO22924358>cl /Zi /EHsc main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
Microsoft (R) Incremental Linker Version 12.00.21005.1
Copyright (C) Microsoft Corporation. All rights reserved.
/out:main.exe
/debug
main.obj
C:\Code\SO22924358>main
~Base()
GetValue() returns 0
Reached end of main
~Base()
Here's the final version of the code I'm working with:
#include <initializer_list>
#include <memory>
#include <iostream>
struct Base
{
virtual int GetValue() { return 0; }
~Base() { std::cerr << "~Base()" << std::endl; }
};
struct Derived1 : public Base
{
int GetValue() override { return 1; }
};
struct Derived2 : public Base
{
int GetValue() override { return 2; }
};
int main()
{
std::initializer_list< std::shared_ptr<Base> > foo
{
std::make_shared<Derived1>(),
std::make_shared<Derived2>()
};
auto iter = std::begin(foo);
std::cerr << "GetValue() returns " << (*iter)->GetValue() << std::endl; // access violation
std::cerr << "Reached end of main" << std::endl;
return 0;
}
Stepping through shows that destructors are called immediately after initializer list construction for shared_ptr<Derived1> (correct, its object has been moved to a shared_ptr<Base>), and the matching shared_ptr<Base>, which is very very wrong.