C++ Initializing a unique_ptr array attribute in a class - c++

I wish to use a unique_ptr for my class here (instead of vector for my own reasons). But I'm not sure how to initialize a new [] array with it. Here is what my code looks like
template <typename T>
class kmap
{
private:
const std::vector<std::string> &data;
std::unique_ptr<T> table;
public:
kmap(std::vector<std::string> &kmers);
};
template <typename T>
kmap<T>::kmap(std::vector<std::string> &kmers) : data(kmers)
{
this->table = std::unique_ptr<T[]>(new T[kmers.size()*2]);
}
Now I can initialize this with std::unique_ptr<T>(new T[kmers.size()*2] ); but I am skeptical that it may only delete the very first element of the array instead of freeing the entire block when the object goes out of scope. Is my skepticism in vain or is it possible to initialize this as an array?
Here is a part of the error message of what I get when initializing this with T =int:
/home/sflash/Documents/misc/kmap.cpp: In instantiation of ‘kmap<T>::kmap(std::vector<std::__cxx11::basic_string<char> >&) [with T = int]’:
/home/sflash/Documents/misc/kmap.cpp:75:23: required from here
/home/sflash/Documents/misc/kmap.cpp:27:15: error: no match for ‘operator=’ (operand types are ‘std::unique_ptr<int, std::default_delete<int> >’ and ‘std::unique_ptr<int [], std::default_delete<int []> >’)
27 | this->table = std::unique_ptr<T[]>(new T[kmers.size()*2]);
| ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/10.2.0/memory:83,
from /home/sflash/Documents/misc/kmap.cpp:2:
/usr/include/c++/10.2.0/bits/unique_ptr.h:371:19: note: candidate: ‘std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]’
371 | unique_ptr& operator=(unique_ptr&&) = default;
| ^~~~~~~~
/usr/include/c++/10.2.0/bits/unique_ptr.h:371:29: note: no known conversion for argument 1 from ‘std::unique_ptr<int [], std::default_delete<int []> >’ to ‘std::unique_ptr<int, std::default_delete<int> >&&’
371 | unique_ptr& operator=(unique_ptr&&) = default;
| ^~~~~~~~~~~~
/usr/include/c++/10.2.0/bits/unique_ptr.h:386:2: note: candidate: ‘template<class _Up, class _Ep> typename std::enable_if<std::__and_<std::__and_<std::is_convertible<typename std::unique_ptr<_Up, _Ep>::pointer, typename std::__uniq_ptr_impl<_Tp, _Dp>::pointer>, std::__not_<std::is_array<_Up> > >, std::is_assignable<_T2&, _U2&&> >::value, std::unique_ptr<_Tp, _Dp>&>::type std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Up, _Ep>&&) [with _Up = _Up; _Ep = _Ep; _Tp = int; _Dp = std::default_delete<int>]’
386 | operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
How I compiled: g++ -pipe -O2 -std=c++14 "$file" -o exe -lm

unique_ptr has a specialization when declared with an array type:
std::unique_ptr<T[]> table;
In this situation, delete[] will be used to delete the pointer.
As a bonus, this specialization also defines an overloaded operator[] to make it easier to use as an array.

Related

Binary operator in std::transform with vector of unique_ptr

I am getting confused with standard library transform when applied to a vector of unique_ptr. I have defined a binary functor addScalar that take 2 const references to unique_ptr and return a const reference to a unique_ptr in order to avoid copying (that is forbidden with unique_ptr).
I then try to use it in a std::transform, but it seems impossible for unique_ptr do undergo binary operation at all, in spite of all my precautions to avoid unique_ptr copying...
Has anybody an idea of how to use std::transform with std::unique_ptr ? Or am I obliged to run through the vector with a for-loop and perform the addition "manually" ? I am also wondering if I could use unique_ptr<const Scalar> in my functor.
Here is my class :
#include "space.h"
#include "scalar.h"
#include <vector>
#include <algorithm>
#include <memory>
using std::vector;
using std::ostream;
using std::unique_ptr;
class addScalar
{
public:
unique_ptr<Scalar> const& operator()(unique_ptr<Scalar> const& scal1, unique_ptr<Scalar> const& scal2)
{
*scal1 += *scal2;
return scal1;
};
};
class Tensor4D
{
public:
Tensor4D(Space& space_in, int ncomp);
Tensor4D(const Tensor4D& tens);
Tensor4D& operator=(const Tensor4D& tens);
size_t size() const {return comp.size();};
~Tensor4D();
protected:
Space* const space;
vector<unique_ptr<Scalar>> comp;
public:
Tensor4D& operator+=(const Tensor4D& tens);
};
and here is the implementation of operator+= :
Tensor4D& Tensor4D::operator+=(const Tensor4D& tens)
{
assert(comp.size() == tens.comp.size());
transform(tens.comp.begin(), tens.comp.end(), comp.begin(), tens.comp.begin(), addScalar());
return *this;
}
I get the following ugly compiler errors :
/usr/include/c++/4.8/bits/stl_algo.h: In instantiation of ‘_OIter std::transform(_IIter1, _IIter1, _IIter2, _OIter, _BinaryOperation) [with _IIter1 = __gnu_cxx::__normal_iterator<const std::unique_ptr<Scalar>*, std::vector<std::unique_ptr<Scalar> > >; _IIter2 = __gnu_cxx::__normal_iterator<std::unique_ptr<Scalar>*, std::vector<std::unique_ptr<Scalar> > >; _OIter = __gnu_cxx::__normal_iterator<const std::unique_ptr<Scalar>*, std::vector<std::unique_ptr<Scalar> > >; _BinaryOperation = addScalar]’:
tensor4D.C:44:94: required from here
/usr/include/c++/4.8/bits/stl_algo.h:4965:12: error: no match for ‘operator=’ (operand types are ‘const std::unique_ptr<Scalar>’ and ‘const std::unique_ptr<Scalar>’)
*__result = __binary_op(*__first1, *__first2);
^
/usr/include/c++/4.8/bits/stl_algo.h:4965:12: note: candidates are:
In file included from /usr/include/c++/4.8/memory:81:0,
from /home/gmartinon/Kadath/C++/Include/scalar.h:27,
from tensor4D.h:5,
from tensor4D.C:1:
/usr/include/c++/4.8/bits/unique_ptr.h:190:7: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = Scalar; _Dp = std::default_delete<Scalar>]
operator=(unique_ptr&& __u) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:190:7: note: no known conversion for argument 1 from ‘const std::unique_ptr<Scalar>’ to ‘std::unique_ptr<Scalar>&&’
/usr/include/c++/4.8/bits/unique_ptr.h:203:2: note: template<class _Up, class _Ep> typename std::enable_if<std::__and_<std::is_convertible<typename std::unique_ptr<_Up, _Ep>::pointer, typename std::unique_ptr<_Tp, _Dp>::_Pointer::type>, std::__not_<std::is_array<_Up> > >::value, std::unique_ptr<_Tp, _Dp>&>::type std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Up, _Ep>&&) [with _Up = _Up; _Ep = _Ep; _Tp = Scalar; _Dp = std::default_delete<Scalar>]
operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:203:2: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/4.8/algorithm:62:0,
from tensor4D.h:8,
from tensor4D.C:1:
/usr/include/c++/4.8/bits/stl_algo.h:4965:12: note: types ‘std::unique_ptr<_Tp, _Dp>’ and ‘const std::unique_ptr<Scalar>’ have incompatible cv-qualifiers
*__result = __binary_op(*__first1, *__first2);
^
In file included from /usr/include/c++/4.8/memory:81:0,
from /home/gmartinon/Kadath/C++/Include/scalar.h:27,
from tensor4D.h:5,
from tensor4D.C:1:
/usr/include/c++/4.8/bits/unique_ptr.h:211:7: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::nullptr_t) [with _Tp = Scalar; _Dp = std::default_delete<Scalar>; std::nullptr_t = std::nullptr_t]
operator=(nullptr_t) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:211:7: note: no known conversion for argument 1 from ‘const std::unique_ptr<Scalar>’ to ‘std::nullptr_t’
/usr/include/c++/4.8/bits/unique_ptr.h:274:19: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Scalar; _Dp = std::default_delete<Scalar>] <near match>
unique_ptr& operator=(const unique_ptr&) = delete;
^
/usr/include/c++/4.8/bits/unique_ptr.h:274:19: note: no known conversion for implicit ‘this’ parameter from ‘const std::unique_ptr<Scalar>*’ to ‘std::unique_ptr<Scalar>*’
The return type of addScalar will be assigned to a unique_ptr<Scalar> so it cannot return a const reference because unique_ptr has no copy assignment. So you will have to return by-value to invoke the move assignment.
To avoid constructing a new Scalar you could use a std:move_iterator to move into addScalar and then move assign to overwrite the moved from value:
class addScalar
{
public:
unique_ptr<Scalar> operator()(unique_ptr<Scalar> scal1,
unique_ptr<Scalar> const& scal2) {
*scal1 += *scal2;
return scal1;
};
};
Tensor4D& Tensor4D::operator+=(const Tensor4D& tens)
{
assert(comp.size() == tens.comp.size());
transform(make_move_iterator(comp.begin()), make_move_iterator(comp.end()),
tens.comp.begin(), comp.begin(), addScalar());
return *this;
}
Andrey makes a good point, it is not clear if this is strictly allowed according to the standard. I'll leave that to a language lawyer. See also this answer.
Live demo.
The C++ standard for std::transform says:
binary_op must not invalidate any iterators, including the end
iterators, or modify any elements of the ranges involved.
The best way for you is to implement your own transform function for specific needs.

Error with `std::vector< std::unique_ptr< T > >`

I'm seeing some errors passing std::vector< std::unique_ptr< T > > around with std::move. The code that reproduces the problem is this:
#include <memory> // for std::unique_ptr
#include <utility> // for std::move
#include <vector> // for std::vector
struct bar {};
using vtype = std::vector<std::unique_ptr<bar>>;
struct foo
{
foo(vtype v) : _v(std::move(v)) { }
private:
vtype _v;
};
vtype getVector()
{
return { std::move( std::unique_ptr<bar>(new bar()) ) };
};
int main()
{
foo f(std::move(getVector()));
};
With clang 3.4, this code produces this error:
$ clang++ -std=c++11 test.cpp -o xtest
In file included from test.cpp:1:
In file included from /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/memory:64:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_construct.h:75:38: error: call to deleted constructor of
'std::unique_ptr<bar, std::default_delete<bar> >'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization
'std::_Construct<std::unique_ptr<bar, std::default_delete<bar> >, const std::unique_ptr<bar, std::default_delete<bar> > &>' requested here
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:117:2: note: in instantiation of function template specialization
'std::__uninitialized_copy<false>::__uninit_copy<const std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar, std::default_delete<bar> > *>'
requested here
__uninit_copy(__first, __last, __result);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_uninitialized.h:258:19: note: in instantiation of function template specialization
'std::uninitialized_copy<const std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar, std::default_delete<bar> > *>' requested here
{ return std::uninitialized_copy(__first, __last, __result); }
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_vector.h:1204:11: note: in instantiation of function template specialization
'std::__uninitialized_copy_a<const std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar, std::default_delete<bar> > *, std::unique_ptr<bar,
std::default_delete<bar> > >' requested here
std::__uninitialized_copy_a(__first, __last,
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/stl_vector.h:368:2: note: in instantiation of function template specialization
'std::vector<std::unique_ptr<bar, std::default_delete<bar> >, std::allocator<std::unique_ptr<bar, std::default_delete<bar> > > >::_M_range_initialize<const
std::unique_ptr<bar, std::default_delete<bar> > *>' requested here
_M_range_initialize(__l.begin(), __l.end(),
^
test.cpp:17:12: note: in instantiation of member function 'std::vector<std::unique_ptr<bar, std::default_delete<bar> >, std::allocator<std::unique_ptr<bar,
std::default_delete<bar> > > >::vector' requested here
return { std::move( std::unique_ptr<bar>(new bar()) ) };
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:273:7: note: function has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete;
^
1 error generated.
The situations doesn't seem to be any better with g++ 4.8:
$ g++-4.8 -std=c++11 test.cpp -o xtest
In file included from /usr/include/c++/4.8/memory:64:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<bar>; _Args = {const std::unique_ptr<bar, std::default_delete<bar> >&}]’:
/usr/include/c++/4.8/bits/stl_uninitialized.h:75:53: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<bar>*; _ForwardIterator = std::unique_ptr<bar>*; bool _TrivialValueTypes = false]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:117:41: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<bar>*; _ForwardIterator = std::unique_ptr<bar>*]’
/usr/include/c++/4.8/bits/stl_uninitialized.h:258:63: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const std::unique_ptr<bar>*; _ForwardIterator = std::unique_ptr<bar>*; _Tp = std::unique_ptr<bar>]’
/usr/include/c++/4.8/bits/stl_vector.h:1206:27: required from ‘void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const std::unique_ptr<bar>*; _Tp = std::unique_ptr<bar>; _Alloc = std::allocator<std::unique_ptr<bar> >]’
/usr/include/c++/4.8/bits/stl_vector.h:369:36: required from ‘std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = std::unique_ptr<bar>; _Alloc = std::allocator<std::unique_ptr<bar> >; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<std::unique_ptr<bar> >]’
test.cpp:17:59: required from here
/usr/include/c++/4.8/bits/stl_construct.h:75:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = bar; _Dp = std::default_delete<bar>]’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
In file included from /usr/include/c++/4.8/memory:81:0,
from test.cpp:1:
/usr/include/c++/4.8/bits/unique_ptr.h:273:7: error: declared here
unique_ptr(const unique_ptr&) = delete;
^
According to this answer and the comments, this shouldn't be happening on these compilers, but I'm not doing exactly the same: I'm trying to initialize the vector with an initializer list.
Any idea what needs to happen in order for this code to build correctly?
The used of the braced-init-list in the return statement within getVector
return { std::move( std::unique_ptr<bar>(new bar()) ) };
results in a call to the std::vector<T> constructor that takes an initializer_list<T> argument. Even though you're moving the unique_ptr, an initializer_list only allows const access to its elements, due to which the vector will attempt to copy the unique_ptr, leading to the error you see.
You can fix the error by resorting to a more verbose manner of constructing the vector
vtype getVector()
{
vtype v;
v.push_back(std::unique_ptr<bar>(new bar()));
return v;
}
Live demo
For curiosity's sake, it is possible to construct a vector from an array of move-only objects, but you need to go through std::move_iterator to move the elements.
vtype getVector()
{
std::unique_ptr<bar> arr[] = {std::unique_ptr<bar>(new bar())};
return {std::make_move_iterator(std::begin(arr)),
std::make_move_iterator(std::end(arr))};
}
your vtype is a vector of unique pointers and you returning to it a unique pointer.
since you are doing the move in the constructor foo you dont need to return a move
take a look to this code:
#include <memory> // for std::unique_ptr
#include <utility> // for std::move
#include <vector> // for std::vector
struct bar {};
using vtype = /*std::vector<*/std::unique_ptr<bar>/*>*/;
struct foo
{
foo(vtype v) : _v(std::move(v)) { }
private:
vtype _v;
};
vtype getVector()
{
return /*{ std::move(*/ std::unique_ptr<bar>(new bar()) /*) }*/;
};
int main()
{
foo f(std::move(getVector()));
};

Correct syntax to assign unique_ptr to new boost::asio::io_service::work object?

What's the correct syntax to assign a std::unique_ptr<boost::asio::io_service::work> pointer to a new boost::asio::io_service::work object?
I am converting boost::asio code from using static functions and global variables to a class implementation, but this means I have to replace the working initialization:
std::unique_ptr<boost::asio::io_service::work> KeepAlive(
new boost::asio::io_service::work(TheASIOService));
with a class member variable in a class definition:
std::unique_ptr<boost::asio::io_service::work> KeepAlive;
later assigned to a new value:
KeepAlive = new boost::asio::io_service::work(IO_Service);
Which the compiler rejects based on the parameter type (brace for Boost error):
g++ -std=c++0x -c -Wall ../comms/CommServer.cpp
../comms/CommServer.cpp: In member function ‘void CommServer::Listen()’:
../comms/CommServer.cpp:39:13: error: no match for ‘operator=’ (operand types are ‘std::unique_ptr<boost::asio::io_service::work>’ and ‘boost::asio::io_service::work*’)
KeepAlive = new boost::asio::io_service::work(IO_Service);
^
../comms/CommServer.cpp:39:13: note: candidates are:
In file included from /usr/include/c++/4.8/memory:81:0,
from /usr/include/boost/config/no_tr1/memory.hpp:21,
from /usr/include/boost/get_pointer.hpp:14,
from /usr/include/boost/bind/mem_fn.hpp:25,
from /usr/include/boost/mem_fn.hpp:22,
from /usr/include/boost/bind/bind.hpp:26,
from /usr/include/boost/bind.hpp:22,
from ../comms/Comms.h:16,
from ../comms/CommServer.h:16,
from ../comms/CommServer.cpp:10:
/usr/include/c++/4.8/bits/unique_ptr.h:190:7: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = boost::asio::io_service::work; _Dp = std::default_delete<boost::asio::io_service::work>]
operator=(unique_ptr&& __u) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:190:7: note: no known conversion for argument 1 from ‘boost::asio::io_service::work*’ to ‘std::unique_ptr<boost::asio::io_service::work>&&’
/usr/include/c++/4.8/bits/unique_ptr.h:203:2: note: template<class _Up, class _Ep> typename std::enable_if<std::__and_<std::is_convertible<typename std::unique_ptr<_Up, _Ep>::pointer, typename std::unique_ptr<_Tp, _Dp>::_Pointer::type>, std::__not_<std::is_array<_Up> > >::value, std::unique_ptr<_Tp, _Dp>&>::type std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Up, _Ep>&&) [with _Up = _Up; _Ep = _Ep; _Tp = boost::asio::io_service::work; _Dp = std::default_delete<boost::asio::io_service::work>]
operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:203:2: note: template argument deduction/substitution failed:
../comms/CommServer.cpp:39:13: note: mismatched types ‘std::unique_ptr<_Tp, _Dp>’ and ‘boost::asio::io_service::work*’
KeepAlive = new boost::asio::io_service::work(IO_Service);
^
In file included from /usr/include/c++/4.8/memory:81:0,
from /usr/include/boost/config/no_tr1/memory.hpp:21,
from /usr/include/boost/get_pointer.hpp:14,
from /usr/include/boost/bind/mem_fn.hpp:25,
from /usr/include/boost/mem_fn.hpp:22,
from /usr/include/boost/bind/bind.hpp:26,
from /usr/include/boost/bind.hpp:22,
from ../comms/Comms.h:16,
from ../comms/CommServer.h:16,
from ../comms/CommServer.cpp:10:
/usr/include/c++/4.8/bits/unique_ptr.h:211:7: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::nullptr_t) [with _Tp = boost::asio::io_service::work; _Dp = std::default_delete<boost::asio::io_service::work>; std::nullptr_t = std::nullptr_t]
operator=(nullptr_t) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:211:7: note: no known conversion for argument 1 from ‘boost::asio::io_service::work*’ to ‘std::nullptr_t’
I checked the documentation for the unique_ptr's assignment operator and see that the example for some reason wraps an assigned value in a call to std::move(), but the compiler balks at that in a similar way:
g++ -std=c++0x -c -Wall ../comms/CommServer.cpp
../comms/CommServer.cpp: In member function ‘void CommServer::Listen()’:
../comms/CommServer.cpp:39:13: error: no match for ‘operator=’ (operand types are ‘std::unique_ptr<boost::asio::io_service::work>’ and ‘std::remove_reference<boost::asio::io_service::work*>::type {aka boost::asio::io_service::work*}’)
KeepAlive = std::move(new boost::asio::io_service::work(IO_Service));
^
../comms/CommServer.cpp:39:13: note: candidates are:
In file included from /usr/include/c++/4.8/memory:81:0,
from /usr/include/boost/config/no_tr1/memory.hpp:21,
from /usr/include/boost/get_pointer.hpp:14,
from /usr/include/boost/bind/mem_fn.hpp:25,
from /usr/include/boost/mem_fn.hpp:22,
from /usr/include/boost/bind/bind.hpp:26,
from /usr/include/boost/bind.hpp:22,
from ../comms/Comms.h:16,
from ../comms/CommServer.h:16,
from ../comms/CommServer.cpp:10:
/usr/include/c++/4.8/bits/unique_ptr.h:190:7: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = boost::asio::io_service::work; _Dp = std::default_delete<boost::asio::io_service::work>]
operator=(unique_ptr&& __u) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:190:7: note: no known conversion for argument 1 from ‘std::remove_reference<boost::asio::io_service::work*>::type {aka boost::asio::io_service::work*}’ to ‘std::unique_ptr<boost::asio::io_service::work>&&’
/usr/include/c++/4.8/bits/unique_ptr.h:203:2: note: template<class _Up, class _Ep> typename std::enable_if<std::__and_<std::is_convertible<typename std::unique_ptr<_Up, _Ep>::pointer, typename std::unique_ptr<_Tp, _Dp>::_Pointer::type>, std::__not_<std::is_array<_Up> > >::value, std::unique_ptr<_Tp, _Dp>&>::type std::unique_ptr<_Tp, _Dp>::operator=(std::unique_ptr<_Up, _Ep>&&) [with _Up = _Up; _Ep = _Ep; _Tp = boost::asio::io_service::work; _Dp = std::default_delete<boost::asio::io_service::work>]
operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:203:2: note: template argument deduction/substitution failed:
../comms/CommServer.cpp:39:13: note: mismatched types ‘std::unique_ptr<_Tp, _Dp>’ and ‘std::remove_reference<boost::asio::io_service::work*>::type {aka boost::asio::io_service::work*}’
KeepAlive = std::move(new boost::asio::io_service::work(IO_Service));
^
In file included from /usr/include/c++/4.8/memory:81:0,
from /usr/include/boost/config/no_tr1/memory.hpp:21,
from /usr/include/boost/get_pointer.hpp:14,
from /usr/include/boost/bind/mem_fn.hpp:25,
from /usr/include/boost/mem_fn.hpp:22,
from /usr/include/boost/bind/bind.hpp:26,
from /usr/include/boost/bind.hpp:22,
from ../comms/Comms.h:16,
from ../comms/CommServer.h:16,
from ../comms/CommServer.cpp:10:
/usr/include/c++/4.8/bits/unique_ptr.h:211:7: note: std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp, _Dp>::operator=(std::nullptr_t) [with _Tp = boost::asio::io_service::work; _Dp = std::default_delete<boost::asio::io_service::work>; std::nullptr_t = std::nullptr_t]
operator=(nullptr_t) noexcept
^
/usr/include/c++/4.8/bits/unique_ptr.h:211:7: note: no known conversion for argument 1 from ‘std::remove_reference<boost::asio::io_service::work*>::type {aka boost::asio::io_service::work*}’ to ‘std::nullptr_t’
Reading the docs on std::move, I see that there are some new pointer concepts I don't quite grok, and should read up on, but until then, I'd rather like a working ::work object.
Is this unique_ptr protecting me from doing something that would invalidate its guarantees? Should I not be using unique_ptr here? Or do I just not have the right syntax? Should I be using unique_ptr::reset(new ...)?
Use unique_ptr::reset:
keepAlive.reset(new boost::asio::io_service::work(IO_Service));
It's valid to use a unique_ptr for deferred construction, although it would be more ideal to use std/boost optional unless you also explicitly need/want to dynamically allocate it. I'll also note that you should attempt to design your classes so that you can fully construct everything in the constructor and not need deferred construction. But sometimes it's unavoidable.
Check out the documentation for std::unique_ptr<>. You'll find that unique_ptr<>::reset() is your friend:
KeepAlive.reset(new boost::asio::io_service::work(IO_Service));

Unable to compile with unique_ptr and deleter

I'm having trouble with unique_ptr and a deleter. The following won't compile:
unique_ptr<RSA> rsa(RSA_new(), ::RSA_free);
unique_ptr<RSA> rsa(RSA_new(), ptr_fun(RSA_free));
The compile errors are shown below.
shared_ptr worked fine, but I need to release one of those pointers when assigning it into another structure (and shared_ptr is not amicable).
I'm using GCC 4.7 on Debian 7.3 (x64). Everything is fully patched.
Any ideas what I am doing wrong?
../source/ac-pki.cpp:461:46: error: no matching function for call to ‘std::unique_ptr<rsa_st>::unique_ptr(RSA*, void (&)(RSA*))’
../source/ac-pki.cpp:461:46: note: candidates are:
In file included from /usr/include/c++/4.7/memory:86:0,
from /home/jwalton/test/include/ac-common.h:39,
from ../source/ac-pki.cpp:1:
/usr/include/c++/4.7/bits/unique_ptr.h:164:2: note: template<class _Up, class> std::unique_ptr::unique_ptr(std::auto_ptr<_Up>&&)
/usr/include/c++/4.7/bits/unique_ptr.h:164:2: note: template argument deduction/substitution failed:
../source/ac-pki.cpp:461:46: note: mismatched types ‘std::auto_ptr<_Up>’ and ‘RSA* {aka rsa_st*}’
In file included from /usr/include/c++/4.7/memory:86:0,
from /home/jwalton/test/include/ac-common.h:39,
from ../source/ac-pki.cpp:1:
/usr/include/c++/4.7/bits/unique_ptr.h:155:2: note: template<class _Up, class _Ep, class> std::unique_ptr::unique_ptr(std::unique_ptr<_Up, _Ep>&&)
/usr/include/c++/4.7/bits/unique_ptr.h:155:2: note: template argument deduction/substitution failed:
../source/ac-pki.cpp:461:46: note: mismatched types ‘std::unique_ptr<_Up, _Ep>’ and ‘RSA* {aka rsa_st*}’
In file included from /usr/include/c++/4.7/memory:86:0,
from /home/jwalton/test/include/ac-common.h:39,
from ../source/ac-pki.cpp:1:
/usr/include/c++/4.7/bits/unique_ptr.h:142:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = rsa_st; _Dp = std::default_delete<rsa_st>; std::unique_ptr<_Tp, _Dp> = std::unique_ptr<rsa_st>]
/usr/include/c++/4.7/bits/unique_ptr.h:142:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/4.7/bits/unique_ptr.h:136:17: note: constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr(std::nullptr_t) [with _Tp = rsa_st; _Dp = std::default_delete<rsa_st>; std::nullptr_t = std::nullptr_t]
/usr/include/c++/4.7/bits/unique_ptr.h:136:17: note: candidate expects 1 argument, 2 provided
/usr/include/c++/4.7/bits/unique_ptr.h:130:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::remove_reference<_To>::type&&) [with _Tp = rsa_st; _Dp = std::default_delete<rsa_st>; std::unique_ptr<_Tp, _Dp>::pointer = rsa_st*; typename std::remove_reference<_To>::type = std::default_delete<rsa_st>]
/usr/include/c++/4.7/bits/unique_ptr.h:130:7: note: no known conversion for argument 2 from ‘void(RSA*) {aka void(rsa_st*)}’ to ‘std::remove_reference<std::default_delete<rsa_st> >::type&& {aka std::default_delete<rsa_st>&&}’
/usr/include/c++/4.7/bits/unique_ptr.h:125:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::conditional<std::is_reference<_Dp>::value, _Dp, const _Dp&>::type) [with _Tp = rsa_st; _Dp = std::default_delete<rsa_st>; std::unique_ptr<_Tp, _Dp>::pointer = rsa_st*; typename std::conditional<std::is_reference<_Dp>::value, _Dp, const _Dp&>::type = const std::default_delete<rsa_st>&]
/usr/include/c++/4.7/bits/unique_ptr.h:125:7: note: no known conversion for argument 2 from ‘void(RSA*) {aka void(rsa_st*)}’ to ‘std::conditional<false, std::default_delete<rsa_st>, const std::default_delete<rsa_st>&>::type {aka const std::default_delete<rsa_st>&}’
/usr/include/c++/4.7/bits/unique_ptr.h:120:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = rsa_st; _Dp = std::default_delete<rsa_st>; std::unique_ptr<_Tp, _Dp>::pointer = rsa_st*]
/usr/include/c++/4.7/bits/unique_ptr.h:120:7: note: candidate expects 1 argument, 2 provided
/usr/include/c++/4.7/bits/unique_ptr.h:114:17: note: constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr() [with _Tp = rsa_st; _Dp = std::default_delete<rsa_st>]
/usr/include/c++/4.7/bits/unique_ptr.h:114:17: note: candidate expects 0 arguments, 2 provided
../source/ac-pki.cpp:550:91: error: cannot convert ‘std::unique_ptr<rsa_st>::pointer {aka rsa_st*}’ to ‘EVP_PKEY* {aka evp_pkey_st*}’ for argument ‘2’ to ‘int PEM_write_PKCS8PrivateKey(FILE*, EVP_PKEY*, const EVP_CIPHER*, char*, int, int (*)(char*, int, int, void*), void*)’
make: *** [source/ac-pki.o] Error 1
You need to give the type of the deleter as well
std::unique_ptr<RSA, void (*)(RSA*)> rsa(RSA_new(), ::RSA_free);
Otherwise, std::unique_ptr uses std::default_delete
The type of the deleter is part of the unique_ptr's type. So change your code to
std::unique_ptr<RSA, decltype(&::RSA_free)> p(RSA_new(), ::RSA_free);
Or, to cut down on the verbosity
using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
RSA_ptr rsa(RSA_new(), ::RSA_free);

Templated move ctor for wrapped unique_ptr

I want something that's like unique_ptr, but guaranteed (within reason) to be non-null. I wrote this class that contains a unique_ptr, and I wrote this move constructor that I'd hoped would allow me to move-construct one of my pointers from another, as long as the underlying unique_ptr could be similarly move-constructed. So, I first try it with something simple; move-constructing a pointer-to-int from a pointer-to-int.
#include <memory>
#include <utility>
#include <cassert>
template<
typename T
>
class Nonup
{
private:
std::unique_ptr<T> m_ptr;
public:
explicit Nonup( T* p )
: m_ptr( p )
{ assert( p ); }
Nonup( const Nonup& ) = delete;
Nonup& operator=( const Nonup& ) = delete;
template<typename U>
Nonup( Nonup<U>&& old )
:
m_ptr( std::move( old ) )
{}
Nonup& operator=( Nonup&& rhs ) = default;
decltype( *m_ptr ) operator*() const { return *m_ptr; }
};
int main()
{
Nonup<int> first( new int( 42 ) );
Nonup<int> second( std::move( first ) );
return 0;
}
Why does g++ 4.7.0 give errors on the move-construction of the variable second? The output of "g++ -std=c++11 main.cpp" follows.
main.cpp: In instantiation of ‘Nonup<T>::Nonup(Nonup<U>&&) [with U = int; T = int]’:
main.cpp:40:43: required from here
main.cpp:27:37: error: no matching function for call to ‘std::unique_ptr<int, std::default_delete<int> >::unique_ptr(std::remove_reference<Nonup<int>&>::type)’
main.cpp:27:37: note: candidates are:
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/memory:86:0,
from main.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:164:2: note: template<class _Up, class> std::unique_ptr::unique_ptr(std::auto_ptr<_Up>&&)
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:164:2: note: template argument deduction/substitution failed:
main.cpp:27:37: note: ‘std::remove_reference<Nonup<int>&>::type {aka Nonup<int>}’ is not derived from ‘std::auto_ptr<_Up>’
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/memory:86:0,
from main.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:155:2: note: template<class _Up, class _Ep, class> std::unique_ptr::unique_ptr(std::unique_ptr<_Up, _Ep>&&)
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:155:2: note: template argument deduction/substitution failed:
main.cpp:27:37: note: ‘std::remove_reference<Nonup<int>&>::type {aka Nonup<int>}’ is not derived from ‘std::unique_ptr<_Up, _Ep>’
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/memory:86:0,
from main.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:142:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = int; _Dp = std::default_delete<int>]
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:142:7: note: no known conversion for argument 1 from ‘std::remove_reference<Nonup<int>&>::type {aka Nonup<int>}’ to ‘std::unique_ptr<int, std::default_delete<int> >&&’
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:136:17: note: constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr(std::nullptr_t) [with _Tp = int; _Dp = std::default_delete<int>; std::nullptr_t = std::nullptr_t]
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:136:17: note: no known conversion for argument 1 from ‘std::remove_reference<Nonup<int>&>::type {aka Nonup<int>}’ to ‘std::nullptr_t’
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:130:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::remove_reference<_To>::type&&) [with _Tp = int; _Dp = std::default_delete<int>; std::unique_ptr<_Tp, _Dp>::pointer = int*; typename std::remove_reference<_To>::type = std::default_delete<int>]
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:130:7: note: candidate expects 2 arguments, 1 provided
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:125:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::conditional<std::is_reference<_Dp>::value, _Dp, const _Dp&>::type) [with _Tp = int; _Dp = std::default_delete<int>; std::unique_ptr<_Tp, _Dp>::pointer = int*; typename std::conditional<std::is_reference<_Dp>::value, _Dp, const _Dp&>::type = const std::default_delete<int>&]
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:125:7: note: candidate expects 2 arguments, 1 provided
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:120:7: note: std::unique_ptr<_Tp, _Dp>::unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer) [with _Tp = int; _Dp = std::default_delete<int>; std::unique_ptr<_Tp, _Dp>::pointer = int*]
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:120:7: note: no known conversion for argument 1 from ‘std::remove_reference<Nonup<int>&>::type {aka Nonup<int>}’ to ‘std::unique_ptr<int, std::default_delete<int> >::pointer {aka int*}’
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:114:17: note: constexpr std::unique_ptr<_Tp, _Dp>::unique_ptr() [with _Tp = int; _Dp = std::default_delete<int>]
/usr/lib/gcc/x86_64-redhat-linux/4.7.0/../../../../include/c++/4.7.0/bits/unique_ptr.h:114:17: note: candidate expects 0 arguments, 1 provided
Thanks!
You're trying to initialise m_ptr from the other Nonup, rather than its m_ptr. The move constructor's initialiser should be:
m_ptr( std::move( old.m_ptr ) )
^^^^^^