std::vector<bool>::reference with std::exchange - gcc

Is there a technical reason why std::exchange does not work on std::vector::reference or is it a bug in the implementation of GCC and Clang? With MSVC it compiles fine.
I have a setup like this (minimal example)
struct Manager
{
std::vector<bool> lifeTimes;
//Should return the state before trying to kill it
bool kill(std::size_t index)
{
return std::exchange(lifeTimes[index], false);
}
};
std::exchange would make this a really nice one liner but GCC complains about:
error: cannot bind non-const lvalue reference of type ‘std::_Bit_reference&’ to an rvalue of type ‘std::vector::reference’ {aka ‘std::_Bit_reference’}
So it seams it complains about the false since only the second parameter is an rvalue

It is not a bug, MSVC compiles your code because it has an extension which enables binding temporary object (Rvalue) to non-const Lvalue reference.
Below code compiles with MSVC:
void foo(int& i) {}
foo(20); // you are passing Rvalue and it is bound to Lvalue reference
Above code doesn't compile under G++ or CLang, when you add const to make reference to
const Lvalue, it works:
void foo(const int&){}
foo(20); // you can bind Rvalue to const Lvalue reference
A few words about vector. operator[] for vector<T> where T is every type except bool returns T&:
T& vector<T>::operator[](index) // where T is not bool
For bool vector class template has specialization. Values of bool are stored to hold one bit space, because you cannot use address-of operator for one bit, vector<bool>::operator[](index) cannot return reference. vector<bool> has inner proxy class which manipulates bits (call this class as reference).
vector<bool>::reference vector<bool>::operator[](index)
^^^^^^^^^
as you see object of proxy is passed by value.
So when you call
return std::exchange(lifeTimes[index], false);
you are passing temporary objecy (Rvalue) to exchange which takes first argument by reference to non-const Lvalue. This is the cause that G++ discards this code. If you want to compile it you can explicitly create Lvalue object of proxy class and pass it:
bool kill(std::size_t index)
{
std::vector<bool>::reference proxyForBit = lifeTimes[index];
return std::exchange(proxyForBit, false);
}

Related

Remove class member type part from decltype

I ran into I case I had not seen before, while using decltype on a member of a templated class. I wanted to make a nicer make_unique so that changing type on the member does not cause fixing the make_unique calls. I wanted to avoid this using decltype(member)::element_type as the type for make_unique but got an error. Here is a simple snippet that shows the error (and I understand why it is shown):
#include <memory>
template<typename T>
struct foo
{
foo()
{
// g++ gives:
// dependent-name 'decltype (((foo<T>*)this)->foo<T>::p_)::element_type' is parsed as a non-type, but instantiation yields a type
// say 'typename decltype (((foo<T>*)this)->foo<T>::p_)::element_type' if a type is meant
//
// How can I atleast remove the class name from the type?
p_ = std::make_unique<decltype(p_)::element_type>();
// g++ gives:
// dependent-name 'decltype (p)::element_type' is parsed as a non-type, but instantiation yields a type
// say 'typename decltype (p)::element_type' if a type is meant
//
// makes sense since p here is dependent on T
std::unique_ptr<T> p = std::make_unique<decltype(p)::element_type>();
// This one is fine, makes sense, since the type is known
std::unique_ptr<int> p2 = std::make_unique<decltype(p2)::element_type>();
}
std::unique_ptr<T> p_;
};
int main()
{
foo<int> f;
return 0;
}
My question is, is there a nice/pretty way to remove the 'is a member of' ((foo<T>*)this)->foo<T>::p_))part from the decltype value, so that at least I could use the same fix and simply provide typename on the member variable p_ ? The long fix suggested by g++ seems kind of ugly.
5 minutes after posting I had an idea that I could do
p_ = std::make_unique<decltype(std::remove_reference(*p_)::type)>();
but that seems to give a parse error.
You can simply place a typename before decltype().
I mean
p_ = std::make_unique<typename decltype(p_)::element_type>();

Shared pointer to incomplete needs deleter in reset method

I am working with shared_ptr storing pointers of a C library.
Here an example of such a C library containing the header bar.h:
#pragma once
typedef struct Flupp MyFlupp;
MyFlupp *
create_flupp();
void
del_flupp(MyFlupp * fp);
void
print_flupp(MyFlupp * f);
Here the struct has a forward declaration and is defined in the bar.so.
I am using the bar.so in my C++ code:
#include <memory>
extern "C"{
#include "bar.h"
}
int main()
{
std::shared_ptr<MyFlupp> flupp_ptr(nullptr, del_flupp);
flupp_ptr.reset(create_flupp());
print_flupp(flupp_ptr.get());
return 0;
}
Here I am storing the MyFlupp* in a shared_ptr. On the declaration, MyFlupp* is unknown and set to nullptr. Later I am calling the reset operation to set the valid pointer. But when I am compling the code, I get the following error:
In file included from /usr/include/c++/8/bits/shared_ptr.h:52,
from /usr/include/c++/8/memory:81,
from test_foo.cpp:1:
/usr/include/c++/8/bits/shared_ptr_base.h: In instantiation of ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(_Yp*) [with _Yp = Flupp; <template-parameter-2-2> = void; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’:
/usr/include/c++/8/bits/shared_ptr_base.h:1293:4: required from ‘std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> std::__shared_ptr<_Tp, _Lp>::reset(_Yp*) [with _Yp = Flupp; _Tp = Flupp; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2; std::__shared_ptr<_Tp, _Lp>::_SafeConv<_Yp> = void]’
test_foo.cpp:10:35: required from here
/usr/include/c++/8/bits/shared_ptr_base.h:1126:19: error: invalid application of ‘sizeof’ to incomplete type ‘Flupp’
static_assert( sizeof(_Yp) > 0, "incomplete type" );
When I am providing the deleter to the reset operation than it is working.
flupp_ptr.reset(create_flupp(), del_flupp);
Can anybody explain me whats going on? I already looked #cppreference but I does not found an answer.
The problem is that the type Flupp has only been forward-declared, but not defined. In the context of the use here, it is considered an incomplete type.
This has certain implications for the use with std::shared_ptr:
std::shared_ptr may be used with an incomplete type T. However, the
constructor from a raw pointer (template<class Y> shared_ptr(Y*)) and
the template<class Y> void reset(Y*) member function may only be
called with a pointer to a complete type (note that std::unique_ptr
may be constructed from a raw pointer to an incomplete type).
Source: cppreference.com
Instead you need to use the respective overloads that accept a pointer and the deleter as arguments.
With unique_ptr this is not necessary, as that one stores the custom deleter as part of the type. But with shared_ptr the deleter is type-erased and only recovered at runtime. This allows you to change the deleter of an existing shared_ptr when calling reset. For this reason you always need to re-state which deleter to use whenever you're calling reset. If no deleter is given, each call to reset will also implicitly reset the deleter to just calling delete on the managed pointer.
So to make it work, just change your reset call to
flupp_ptr.reset(create_flupp(), del_flupp);

why the type of obj.i and std::forward<int>(obj.i) are different?

According to Is a member of an rvalue structure an rvalue or lvalue?:
if E1 is lvalue, then E1.E2 is lvalue, and forward cast its argument to an rvalue only if that argument is bound to an rvalue. In function void foo(Obj &&obj) below, obj is lvalue, so obj.i is lvalue, why is std::forward<int>(obj.i) an rvalue?
class Obj
{
public:
int i;
};
void foo(int &i)
{
cout<<"foo(int&)"<<endl;
}
void foo(int &&i)
{
cout<<"foo(int&&)"<<endl;
}
void foo(Obj &&obj)
{
foo(std::forward<int>(obj.i));
foo(obj.i);
}
int main()
{
Obj obj;
foo(std::move(obj));
return 0;
}
output
foo(int&&)
foo(int&)
You're actually forwarding the wrong thing. In this function:
void foo(Obj &&obj)
{
foo(std::forward<int>(obj.i));
foo(obj.i);
}
obj has name, so it's an lvalue. obj.i is an lvalue in both cases, just in the first you're explicitly casting it to an rvalue! When forward gets a reference type, you get out an lvalue. When it gets a non-reference type, you get an rvalue. You're giving it a non-reference type, so the forward here is equivalent to: foo(std::move(obj.i)); Does that make it clearer why you get an rvalue?
The question you linked, however, is about members of rvalues. To get that, you need to turn obj itself into an rvalue:
foo(std::move(obj).i);
Here, since std::move(obj) is an rvalue, std::move(obj).i is an rvalue as well.
Regardless, using forward when taking an argument by not-forwarding-reference is a little weird. Here's a more general example:
template <class O>
void foo(O&& obj) {
foo(std::forward<O>(obj).i);
}
foo(obj); // calls foo(int&)
foo(std::move(obj)); // calls foo(int&&)
In the former case, std::forward<O>(obj) is an lvalue because O is Obj&, which makes std::foward<O>(obj).i an lvalue. In the latter case, they're both rvalues.
function void foo(Obj &&obj) below:
obj is lvalue, so obj.i is lvalue,
Hmm, do you declare obj as rvalue reference in function proto and then insist it's an lvalue?
According to the specifications of std::forward, the return type is simple an rvalue reference applied to the template type. So the return type std::forward<int> is int&& - used in this way it has exactly the same effect as std::move.
The normal recipe for using std::forward is with universal references:
template<typename T>
void f(T&& val)
{
other_func(std::forward<T>(val));
}
This would work correctly for references, as the deduced type for an lvalue reference in this case would also be an lvalue reference. In your case you're hard-coding the type (to int) rather than deducing it - the deduced type if you used the above pattern would in fact be int&, not int.
You will see that if you change foo(std::forward<int>(obj.i)) to foo(std::forward<int&>(obj.i)) you will get what you expect

C++ Do I always have to use std::move to invoke the move constructor?

"Theory" question if you will.
In order to execute/make use of the move constructor in a class, do I always have to use std::move(...) to tell the compiler that I wish to 'move' an object rather than copy it?
Are there any cases where the compiler will invoke the move constructor for me without the use of std::move? (My guess would be in function return values?)
According to cppreference.com (http://en.cppreference.com/w/cpp/language/move_constructor):
The move constructor is called whenever an object is initialized from xvalue of the same type, which includes
initialization, T a = std::move(b); or T a(std::move(b));, where b is of type T;
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t);
function return: return a; inside a function such as T f(), where a is of type T which has a move constructor.
In most cases, yes std::move is needed.
The compiler will invoke the move constructor without std::move when:
returning a local variable by value
when constructing an object from an rvalue of the same type
In all other cases, use std::move. E.g.:
struct S {
std::string name;
S(std::string name) : name(std::move(name)) {}
};
and
std::unique_ptr<Base> func() {
auto p = std::make_unique<Derived>();
return std::move(p); // doesn't work without std::move
}
std::move is just a cast.
unique_ptr<int> global;
auto v = unique_ptr<int>(global); // global is a lvalue, therefore the
unique_ptr(unique_ptr<T>&v) constructor that accepts lvalue references is called.
auto v = unique_ptr<int>(std::move(global)); // move returns a &&rvalue reference, therefore the
unique_ptr(unique_ptr<T>&&v) constructor that accepts &&rvalue references is used.
When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
therefore,
unique_ptr<int> hello()
{
unique_ptr<int> local;
return local;
// local is an lvalue, but since the critera for elision is met,
// the returned object is created using local as if it was an rvalue
}
Also,
unique_ptr<int> hello = std::unique_ptr<int>();
// we have a pure rvalue in the right, therefore no std::move() cast is needed.

What is an rvalue reference to function type?

I have recently wrapped my mind around the C++0x's concepts of glvalues, xvalues and prvalues, as well as the rvalue references. However, there's one thing which still eludes me:
What is "an rvalue reference to function type"? It is literally mentioned many times in the drafts. Why was such a concept introduced? What are the uses for it?
I hate to be circular, but an rvalue reference to function type is an rvalue reference to function type. There is such a thing as a function type, e.g. void (). And you can form an rvalue reference to it.
In terms of the classification system introduced by N3055, it is an xvalue.
Its uses are rare and obscure, but it is not useless. Consider for example:
void f() {}
...
auto x = std::ref(f);
x has type:
std::reference_wrapper<void ()>
And if you look at the synopsis for reference_wrapper it includes:
reference_wrapper(T&) noexcept;
reference_wrapper(T&&) = delete; // do not bind to temporary objects
In this example T is the function type void (). And so the second declaration forms an rvalue reference to function type for the purpose of ensuring that reference_wrapper can't be constructed with an rvalue argument. Not even if T is const.
If it were not legal to form an rvalue reference to function, then this protection would result in a compile time error even if we did not pass an rvalue T to the constructor.
In the old c++ standard the following is forbidden:
int foo();
void bar(int& value);
int main()
{
bar(foo());
}
because the return type of foo() is an rvalue and is passed by reference to bar().
This was allowed though with Microsoft extensions enabled in visual c++ since (i think) 2005.
Possible workarounds without c++0x (or msvc) would be declaring
void bar(const int& value);
or using a temp-variable, storing the return-value of foo() and passing the variable (as reference) to bar():
int main()
{
int temp = foo();
bar(temp);
}

Resources