Consider the following example:
#include <iostream>
#include <memory>
#include <boost/optional.hpp>
struct X {
boost::optional<std::unique_ptr<int>> foo(int i) {
std::unique_ptr<int> t(new int{i});
return t;
// every compiler works with:
// return std::unique_ptr<int>(new int{i});
}
};
struct Y {
X x;
boost::optional<std::unique_ptr<int>> bar(int i) {
return x.foo(i);
}
};
int main()
{
Y{}.bar(42);
}
I have two chained copy-elidable returns of a non-copyable object. gcc 5.2 compiles it fine. But neither gcc 4.8 nor clang 3.7 like it, due to actually trying to perform the copy. Which compiler is right? I'm assuming gcc intentionally changed to allow this behavior, which seems particularly useful for cases like this.
Related
Aliasing is the main optimization issue with pass-by-const-reference functions that take multiple arguments; this has been pointed out in answers to similar questoins. However, this particular answer also seems to suggest that the compiler can make more assumptions about a single-argument pass-by-value function than an otherwise identical pass-by-const-reference function.
This would imply that pass-by-value is the preferred strategy for small objects. While this seems like perfectly sound logic for an argument of a non-class type or a type with a constexpr copy constructor, I fail to see how these assumptions could be extended to an argument that does not meet these requirements. Take the following example:
foo.h
#ifndef FOO_H
#define FOO_H
struct Foo {
int i;
Foo(int i);
Foo(const Foo &other);
~Foo();
};
void incrementNewestFoo();
#endif
foo.cpp
#include "foo.h"
#include <algorithm>
#include <vector>
std::vector<Foo *> foos;
Foo::Foo(int i) : i(i) {
foos.push_back(this);
}
Foo::Foo(const Foo &other) : i(other.i) {
foos.push_back(this);
}
Foo::~Foo() {
foos.erase(std::find(foos.cbegin(), foos.cend(), this));
}
void incrementNewestFoo() {
foos.back()->i += 1;
}
main.cpp
#include "foo.h"
#include <iostream>
using namespace std;
void passByValue(Foo foo) {
cout << foo.i << endl;
incrementNewestFoo();
cout << foo.i << endl;
}
void passByConstReference(const Foo &foo) {
cout << foo.i << endl;
incrementNewestFoo();
cout << foo.i << endl;
}
int main() {
Foo foo(0);
passByValue(foo);
passByConstReference(foo);
}
As far as I'm aware, the behavior of main() is well-defined according to the C++ standard. According to the previously linked answer, the most valuable assumption to be made about the pass-by-value function is that the value of i will not change between reads in most cases. However, it appears to me as though this assumption is invalid for any call to a function whose definition resides in another compilation unit (except for const member functions for which the pass-by-const-reference function can make the same assumption anyway). Perhaps I am missing something?
See here: https://godbolt.org/z/MW3jW6
Using gcc 8.4 (8.3 in godbolt) my code is failing to compile. With gcc 10.x it seems to compile ok. But I can't figure out what the error is telling me exactly...
Here is the code for reference:
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <unordered_map>
class non_copy
{
public:
non_copy() = default;
non_copy(non_copy&&){}
non_copy(non_copy&) = delete;
private:
std::mutex m;
std::condition_variable cv;
};
class item
{
public:
item() = default;
item(item&&){}
private:
non_copy m_item; // Works if I remove this
};
bool test()
{
// does not work
std::unordered_map<int, item> map; // Error here
map.emplace(1, item());
// Works ok
std::unordered_map<int, std::string> map2;
map2.emplace(1, "test");
return true;
}
int main()
{
std::cout << "test" << std::endl;
return test();
}
If I remove the non_coom m_item from item class then it compiles. I figured that I just needed to create move constructors, but that was not enough. map.emplace - so I read - should take rvalue refs (move c'tor) as parameters. so I think I just got a bit confused here.
What I want to be able to do is just insert new items into the map - I don't care about coping the contents - so the mutex and condition var will just be default constructed.
Note: see the error I get in the link - its quite large, but I can copy it into here if needed
Your copy constructor (with const), even if legal is unusual, and appears to be problematic.
Use regular one:
non_copy(const non_copy&) = delete;
Consider the following code segment which looks very innocent:
#include <functional>
#include <iostream>
#include <list>
#include <memory>
struct foo;
std::list<std::weak_ptr<foo>> wptrs;
std::function<void()> global_func;
struct foo {
int &a;
foo(int &a) : a{ a } { }
~foo() {
global_func = [&] {
wptrs.remove_if([](auto &x) { return x.expired(); });
std::cout << "a= " << a << std::endl;
};
}
};
int main() {
int a = 5;
auto ptr = std::make_shared<foo>(a);
wptrs.emplace_back(ptr);
ptr = nullptr; // object is destroyed here
global_func();
return 0;
}
When I first encountered the problem on MSVC (Visual Studio 2017), I was working on a TCP/IP server, which tries to clean up a list of weak_ptrs to connection objects. The connection object schedules a lambda to clear a list of weak_ptrs of connections by calling weak_ptr<T>::expired(). I was happy before because everything used to work fine when compiled with clang-6.0+ or g++-7+. Then, I had to use MSVC and received a Read Access violation when destruct was called. I was shocked and tried to generate a minimal example exhibiting the same problem. The minimal example is given above.
The minimal example made the error messages clear, and it seemed like MSVC lambda tries to access to this->__this->a. This access sequence suggests that MSVC does not capture the address of a (or a reference to a), but rather it captures the address of *this and acquires access to a using this object. Since the *this object is completely deallocated when weak ref count becomes zero, I have a memory access error.
Obviously, MSVC approach is radically different than the approach of g++ and clang. So, my question is which compiler is right?
P.S. A simple fix for MSVC case:
#include <functional>
#include <iostream>
#include <list>
#include <memory>
struct foo;
std::list<std::weak_ptr<foo>> wptrs;
std::function<void()> global_func;
struct foo {
int &a;
foo(int &a) : a{ a } { }
~foo() {
global_func = [a = &a] { // capture a ptr instead
wptrs.remove_if([](auto &x) { return x.expired(); });
std::cout << "a= " << *a << std::endl;
};
}
};
int main() {
int a = 5;
auto ptr = std::make_shared<foo>(a);
wptrs.emplace_back(ptr);
ptr = nullptr; // object is destroyed here
global_func();
return 0;
}
Members of *this are never captured: they can’t be explicitly captured, and using them implicitly captures *this (by reference regardless of the capture-default; consider using [=,this] for clarity). So your second example is the only correct way to do it; GCC and Clang may have been optimizing away the use of foo::a since references can’t be rebound.
Is it normal that this compiles fine?
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> buf;
generate(buf.begin(), buf.end(), []{ return 0; });
}
(Note the missing std:: in front of generate())
Is this behavior documented somewhere? Or did I stumble across a compiler or library bug? Which in my case would be GCC 5.3.0 and Clang 3.8.0 on Linux; both use libstdc++, so maybe library bug?
This is allowed, essentially because the arguments to generate are in std.
Code like
namespace Foo
{
struct B{};
void foo(const B&);
}
int main()
{
Foo::B b; /*Requires Foo::*/
foo(b); /*Does not require Foo:: as that is gleaned from the argument*/
}
is acceptable for similar reasons. We call it argument dependent lookup. See https://en.wikipedia.org/wiki/Argument-dependent_name_lookup
After I read the reference link: Must provide destructor in the PIMPL, I do the follow the example, but the g++(4.6.1) doesn't generate compile error as I expected
The source code is:
// Predeclare.h
#include <vector>
#include <boost/scoped_ptr.hpp>
#include <iostream>
struct A;
class Predeclare
{
std::vector<A> alist_;
boost::scoped_ptr<A> pa_;
//A a;
public:
Predeclare();
//~Predeclare();
void print();
void set(int i);
};
// Predeclare.cpp
#include "Predeclare.h"
struct A
{
int a;
};
Predeclare::Predeclare(): pa_(new A)
{}
/*
Predeclare::~Predeclare()
{}
*/
void Predeclare::print()
{
std::cout << pa_->a << '\n';
}
void Predeclare::set(int i)
{
pa_->a = i;
}
int main()
{
Predeclare c1;
c1.set(10);
c1.print();
return 0;
}
Then, compile the program
g++ Predeclare.cpp
Everything is ok, Why the g++(4.6.1) doesn't generate compile error?
In your example, the compiler only needs a destructor for Predeclare when it reaches the declaration of c1 in main(). By that point, A has already been defined, earlier in Predeclare.cpp.
If you were to try to instantiate Predeclare in a different source file, you would likely run into problems.
Note that instantiating std::vector<A> where A is incomplete yields undefined behavior. When you instantiate a container, the value type must be Destructible, and only complete types are Destructible.