Templated move ctor for wrapped unique_ptr - c++

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 ) )
^^^^^^

Related

C++ Initializing a unique_ptr array attribute in a class

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.

Where is piecewise_construct for boost::container::map::emplace()?

Using Boost, I am trying to emplace() a key/value pair into a boost::container::map. The value needs multiple arguments for the constructor. From what I've been able to find, I need to use piecewise_construct and pass the constructor args in tuples. This works with std::map<K,V>, but I can't get it to work using boost::container::map<K,V> The closest Boost doc I can find shows boost::unordered_multimap<K,V>, but not a plain map<K,V>.
#include <map>
#include <boost/tuple/tuple.hpp>
#include <boost/container/map.hpp>
#include <boost/unordered_set.hpp>
class A {
public:
/**/ A( int ) { }
bool operator<( const A & ) const { return false; }
} ;
class B {
public:
/**/ B( int, const char * ) { }
} ;
int
main( int, char ** )
{
A a( 100 );
B b( 200, "foo" );
std::map<A,B> mgood;
mgood.emplace( std::piecewise_construct,
std::make_tuple( 100 ),
std::make_tuple( 200, "Hello" ) );
#if 1
boost::container::map<A,B> mbad;
mbad.emplace( boost::unordered::piecewise_construct,
boost::make_tuple( 300 ),
boost::make_tuple( 400, "World" ) );
#endif
}
The g++-4.9.2 error messages are impenetrable (to me, anyway):
make -k tst g++ -DBOOST_LOG_DYN_LINK -g -std=c++11 -c -o tst.o
tst.cc In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp: In
instantiation of âstatic void
boost::container::allocator_traits::priv_construct(boost::move_detail::false_type,
Allocator&, T*, Args&& ...) [with T =
boost::container::container_detail::pair; Args = {const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple}; Allocator =
boost::container::new_allocator, void*, (boost::container::tree_type_enum)0u, true> >;
boost::move_detail::false_type =
boost::move_detail::integral_constant]â:
/usr/local/include/boost/container/allocator_traits.hpp:353:86:
required from âstatic void
boost::container::allocator_traits::construct(Allocator&,
T*, Args&& ...) [with T = boost::container::container_detail::pair; Args = {const boost::unordered::piecewise_construct_t&,
boost::tuples::tuple,
boost::tuples::tuple}; Allocator =
boost::container::new_allocator, void*, (boost::container::tree_type_enum)0u, true> >]â
/usr/local/include/boost/container/detail/node_alloc_holder.hpp:167:81:
required from
âboost::container::container_detail::node_alloc_holder::NodePtr
boost::container::container_detail::node_alloc_holder::create_node(Args&& ...) [with Args = {const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple}; Allocator =
boost::container::new_allocator >; ICont =
boost::intrusive::rbtree_impl, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::rbtree_node_traits,
(boost::intrusive::link_mode_type)0u, boost::intrusive::dft_tag, 3u>,
void,
boost::container::value_to_node_compare, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::tree_value_compare,
std::less,
boost::container::container_detail::select1st >
, long unsigned int, true, void>; boost::container::container_detail::node_alloc_holder::NodePtr =
boost::container::container_detail::tree_node,
void*, (boost::container::tree_type_enum)0u, true>]â
/usr/local/include/boost/container/detail/tree.hpp:922:94: required
from
âstd::pair,
Options:: tree_type, Options:: optimize_size>::type::iterator, false>,
bool> boost::container::container_detail::tree::emplace_unique(Args&& ...) [with Args =
{const boost::unordered::piecewise_construct_t&,
boost::tuples::tuple,
boost::tuples::tuple}; Key = A; T = std::pair;
KeyOfValue =
boost::container::container_detail::select1st >;
Compare = std::less; Allocator =
boost::container::new_allocator >; Options =
boost::container::tree_opt<(boost::container::tree_type_enum)0u,
true>; typename
boost::container::container_detail::intrusive_tree_type,
Options:: tree_type, Options:: optimize_size>::type::iterator =
boost::intrusive::tree_iterator, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::rbtree_node_traits,
(boost::intrusive::link_mode_type)0u, boost::intrusive::dft_tag, 3u>,
false>]â /usr/local/include/boost/container/map.hpp:665:72:
required from âstd::pair, boost::container::container_detail::select1st >, Compare, Allocator, MapOptions>::iterator, bool> boost::container::map::emplace(Args&& ...) [with Args = {const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple}; Key = A; T = B;
Compare = std::less; Allocator =
boost::container::new_allocator >; MapOptions =
boost::container::tree_opt<(boost::container::tree_type_enum)0u,
true>; typename boost::container::container_detail::tree,
boost::container::container_detail::select1st >, Compare, Allocator, MapOptions>::iterator = boost::container::container_detail::iterator_from_iiterator, void*, (boost::container::tree_type_enum)0u, true>,
boost::intrusive::rbtree_node_traits,
(boost::intrusive::link_mode_type)0u, boost::intrusive::dft_tag, 3u>,
false>, false>]â tst.cc:90:53: required from here
/usr/local/include/boost/container/allocator_traits.hpp:408:10: error:
no matching function for call to
âboost::container::container_detail::pair::pair(const
boost::unordered::piecewise_construct_t&, boost::tuples::tuple, boost::tuples::tuple)â
{ ::new((void)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
candidates are: In file included from
/usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:146:4: note:
template
boost::container::container_detail::pair::pair(std::pair<_U1,
_U2>&&)
pair(BOOST_RV_REF_BEG std::pair BOOST_RV_REF_END p)
^ /usr/local/include/boost/container/detail/pair.hpp:146:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
types âstd::pair<_T1, _T2>â and âconst
boost::unordered::piecewise_construct_tâ have incompatible
cv-qualifiers
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:141:4: note:
boost::container::container_detail::pair::pair(std::pair<_T1,
_T2>&&) [with T1 = A; T2 = B]
pair(BOOST_RV_REF_BEG std::pair BOOST_RV_REF_END p)
^ /usr/local/include/boost/container/detail/pair.hpp:141:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:137:4: note:
template
boost::container::container_detail::pair::pair(const
std::pair<_U1, _U2>&)
pair(const std::pair& p)
^ /usr/local/include/boost/container/detail/pair.hpp:137:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
âconst boost::unordered::piecewise_construct_tâ is not derived
from âconst std::pair<_T1, _T2>â
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:132:4: note:
boost::container::container_detail::pair::pair(const
std::pair<_T1, _T2>&) [with T1 = A; T2 = B]
pair(const std::pair& x)
^ /usr/local/include/boost/container/detail/pair.hpp:132:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:126:4: note:
template
boost::container::container_detail::pair::pair(U&&, V&&)
pair(BOOST_FWD_REF(U) u, BOOST_FWD_REF(V) v)
^ /usr/local/include/boost/container/detail/pair.hpp:126:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
candidate expects 2 arguments, 3 provided
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:120:4: note:
boost::container::container_detail::pair::pair(const T1&,
const T2&) [with T1 = A; T2 = B]
pair(const T1 &t1, const T2 &t2)
^ /usr/local/include/boost/container/detail/pair.hpp:120:4: note: candidate expects 2 arguments, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:115:4: note:
template
boost::container::container_detail::pair::pair(boost::container::container_detail::pair&&)
pair(BOOST_RV_REF_BEG pair BOOST_RV_REF_END p)
^ /usr/local/include/boost/container/detail/pair.hpp:115:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
types âboost::container::container_detail::pairâ and
âconst boost::unordered::piecewise_construct_tâ have incompatible
cv-qualifiers
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:110:4: note:
template
boost::container::container_detail::pair::pair(const
boost::container::container_detail::pair&)
pair(const pair &p)
^ /usr/local/include/boost/container/detail/pair.hpp:110:4: note: template argument deduction/substitution failed: In file included from
/usr/local/include/boost/container/detail/tree.hpp:25:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/allocator_traits.hpp:408:10: note:
âconst boost::unordered::piecewise_construct_tâ is not derived
from âconst boost::container::container_detail::pairâ
{ ::new((void*)p, boost_container_new_t()) T(::boost::forward(args)...); }
^ In file included from /usr/local/include/boost/container/detail/tree.hpp:36:0,
from /usr/local/include/boost/container/map.hpp:30,
from tst.cc:59: /usr/local/include/boost/container/detail/pair.hpp:105:4: note:
boost::container::container_detail::pair::pair(boost::container::container_detail::pair&&) [with T1
= A; T2 = B]
pair(BOOST_RV_REF(pair) p)
^ /usr/local/include/boost/container/detail/pair.hpp:105:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:100:4: note:
boost::container::container_detail::pair::pair(const
boost::container::container_detail::pair&) [with T1 = A; T2 =
B]
pair(const pair& x)
^ /usr/local/include/boost/container/detail/pair.hpp:100:4: note: candidate expects 1 argument, 3 provided
/usr/local/include/boost/container/detail/pair.hpp:95:4: note:
boost::container::container_detail::pair::pair() [with T1 = A;
T2 = B]
pair()
^ /usr/local/include/boost/container/detail/pair.hpp:95:4: note: candidate expects 0 arguments, 3 provided : recipe for target
'tst.o' failed make: *** [tst.o] Error 1 make: Target 'tst' not remade
because of errors.
Compilation exited abnormally with code 2 at Sat Apr 2 17:11:28
Can you point me in a useful direction? (I'd prefer not to mix boost and std containers; it should be possible to emplace into a boost::container::map, right?)
It appears that piecewise_construct is not implemented for boost::pair (which is the type for boost::container::map entries). See .../boost/container/detail/pair.hpp:151:
//piecewise_construct missing
//template <class U, class V> pair(pair<U, V>&& p);
//template <class... Args1, class... Args2>
// pair(piecewise_construct_t, tuple<Args1...> first_args,
// tuple<Args2...> second_args);
I guess the implementation for this is tough.
piecewise_construct support for both C++03 and C++11 capable compilers was added in commit:
https://github.com/boostorg/container/commit/79a75f470e75f35f5f2a91e10fcc67d03b0a2160
and will be officially released in Boost 1.62. The following code compiles fine:
#include <boost/tuple/tuple.hpp>
#include <tuple>
#include <boost/container/map.hpp>
class A {
public:
/**/ A( int ) { }
bool operator<( const A & ) const { return false; }
} ;
class B {
public:
/**/ B( int, const char * ) { }
} ;
int main( int, char *[] )
{
A a( 100 );
B b( 200, "foo" );
boost::container::map<A,B> m;
//1) Both Boost.Tuple and std tuple supported
//2) std:: or boost::container::piecewise_construct supported
m.emplace( boost::container::piecewise_construct,
boost::make_tuple( 300 ),
boost::make_tuple( 400, "World" ) );
m.emplace( std::piecewise_construct,
std::make_tuple( 400 ),
std::make_tuple( 500, "World2" ) );
}

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()));
};

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);