Defining destructor of base class makes std::map::emplace fail - c++

When I try to emplace an object which has a unique_ptr and a destructor into a map, I get a compiler error, shown below. However, when the object has no destructor defined, emplacement works just fine. What is going on?
How can I emplace an object which has a unique_ptr and a destructor defined into a map?
#include <map>
#include <iostream>
#include <string>
struct A {
std::string name;
std::unique_ptr<int> p;
virtual ~A(){}; // comment out this line
A(std::string n) : name(n) { }
};
int main()
{
std::map<int, A> m;
m.emplace(1, A("Name"));
return 0;
}
I'm using Apple clang version 11.0.0 on OSX
Compiler Error
In file included from main.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/map:480:
In file included from /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__tree:16:
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:1826:31: error: no matching constructor for initialization of 'std::__1::pair<const int, B>'
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:1718:18: note: in instantiation of function template specialization
'std::__1::allocator<std::__1::__tree_node<std::__1::__value_type<int, B>, void *> >::construct<std::__1::pair<const int, B>, int, B>' requested here
{__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/memory:1561:14: note: in instantiation of function template specialization
'std::__1::allocator_traits<std::__1::allocator<std::__1::__tree_node<std::__1::__value_type<int, B>, void *> > >::__construct<std::__1::pair<const int, B>,
int, B>' requested here
{__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__tree:2212:20: note: in instantiation of function template specialization
'std::__1::allocator_traits<std::__1::allocator<std::__1::__tree_node<std::__1::__value_type<int, B>, void *> > >::construct<std::__1::pair<const int, B>,
int, B>' requested here
__node_traits::construct(__na, _NodeTypes::__get_ptr(__h->__value_), _VSTD::forward<_Args>(__args)...);
^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/__tree:2157:29: note: in instantiation of function template specialization
'std::__1::__tree<std::__1::__value_type<int, B>, std::__1::__map_value_compare<int, std::__1::__value_type<int, B>, std::__1::less<int>, true>,
std::__1::allocator<std::__1::__value_type<int, B> > >::__construct_node<int, B>' requested here
__node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
^
... [similar error messages come here]
main.cpp:19:11: note: in instantiation of function template specialization 'std::__1::map<int, B, std::__1::less<int>, std::__1::allocator<std::__1::pair<const
int, B> > >::emplace<int, B>' requested here
m.emplace(1, B("Name"));
^

std::map::emplace allows to create object in place by passing constructor's arguments and you don't need copy/move.
As mentions in one of comment that because of user provided distructor, compiler does not generate move constructor implicitly.
But i have added move because otherwise it not useful any way in real world problems.
Due to this you need to create object in place.
Using following code.
#include <iostream>
#include <map>
#include <memory>
struct A {
std::string name;
std::unique_ptr<int> p;
~A(){}
A(std::string n) : name(n) { }
A(A&& other): name(std::move(other.name)), p(std::move(other.p)){}
};
int main(int , char *[]){
std::map<int, A> m;
m.emplace(1, "Name");
for(const auto& e: m)
cout<< e.first<< ", "<< e.second.name<<'\n';
}
output: 1, Name

Related

Inheritance from STL priority_queue with custom comparator not working

I would like to inherit from STL priority queue to have some additional functionality such as:
allowing removal. But I am struggling to make this work when I use custom comparators. MWE:
#include <queue>
template<typename T, class Container=std::vector<T>, class Compare=std::less<typename Container::value_type>>
class custom_priority_queue : public std::priority_queue<T, Container, Compare>
{
public:
// My additional functions here.
};
int main()
{
auto pq_comp = [](const int& a, const int& b) { return a <= b; };
std::priority_queue<int, std::vector<int>, decltype(pq_comp)> pq(pq_comp); // works
custom_priority_queue<int> pq_custom; // works
custom_priority_queue<int, std::vector<int>, decltype(pq_comp)> pq_custom2(pq_comp); // Error
return 0;
}
The error is:
main.cpp: In function ‘int main()’:
main.cpp:15:87: error: no matching function for call to ‘custom_priority_queue, main():: >::custom_priority_queue(main()::&)’
15 | custom_priority_queue<int, std::vector<int>, decltype(pq_comp)> pq_custom2(pq_comp); // Error
| ^
main.cpp:4:7: note: candidate: ‘custom_priority_queue, main():: >::custom_priority_queue(const custom_priority_queue, main():: >&)’
4 | class custom_priority_queue : public std::priority_queue<T, Container, Compare>
| ^~~~~~~~~~~~~~~~~~~~~
main.cpp:4:7: note: no known conversion for argument 1 from ‘main()::’ to ‘const custom_priority_queue, main():: >&’
main.cpp:4:7: note: candidate: ‘custom_priority_queue, main():: >::custom_priority_queue(custom_priority_queue, main():: >&&)’
main.cpp:4:7: note: no known conversion for argument 1 from ‘main()::’ to ‘custom_priority_queue, main():: >&&’
Constructors are not automatically inherited, so your class probably lacks any constructor, except the implicitly-declared ones.
You can explicitly inherit all constructors of the base class:
template<typename T, class Container=std::vector<T>, class Compare=std::less<typename Container::value_type>>
class custom_priority_queue : public std::priority_queue<T, Container, Compare>
{
// inherit constructors
using priority_queue::priority_queue;
public:
// My additional functions here.
};

C++ Template Inheritance Issues (Warnings and Errors)

I'm trying to mimic the .Net implementation of a generic List in C++.
I've fleshed out the various interfaces as purely virtual abstract classes as follows:
template <typename T>
class ICollection {
public:
virtual void Add(T item) = 0;
virtual void Clear(void) = 0;
virtual bool Contains(T item) = 0;
virtual void Remove(T item) = 0;
virtual int32_t Count(void) const = 0;
};
template <typename T>
class IList : public ICollection<T> {
public:
virtual T Item(int32_t index) = 0;
virtual int32_t IndexOf(T item) = 0;
virtual void Insert(int32_t index, T item) = 0;
virtual void RemoveAt(int32_t index) = 0;
};
Now when I attempt to implement my main List class as follows:
template <typename T>
class List : public IList<T>, public ICollection<T> {
public:
List(void);
List(int32_ capacity);
// ICollection<T>
void Add(T item);
// other functions from ICollection
// IList<T>
T Item(int32_t index);
// other functions from IList
void AddRange(IList<T> items);
private:
typedef vector<T> ListType;
ListType *m_pList;
};
template <typename T>
List<T>::List(void) {
m_pList = new ListType();
}
template <typename T>
void List<T>::Insert(uint32_t index, T item) {
// Insert an entry into the list at the specified offset
m_list->insert(index, item);
}
// Implementation of other functions here...
As soon as I try to use the List<T> class as follows:
List<int32_t> myList;
A warning occurs saying:
In instantiation of 'class List<long int>':
required from here
warning: direct base 'ICollection<long int>' inaccessible in 'List<long int>' due to ambiguity [enabled by default]
class List : public IList<T>, public ICollection<T> {
^
Followed by the following error:
In instantiation of 'void List<T>::Insert(uint32_t, T) [with T = long int; uint32_t = long unsigned int]':
required from here
error: no matching function for call to 'std::vector<long int, std::allocator<long int> >::insert(uint32_t&, long int&)'
m_list->insert(index, item);
^
note: std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::insert(std::vector<_Tp, _Alloc>::iterator, const value_type&) [with _Tp = long int; _Alloc = std::allocator<long int>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<long int*, std::vector<long int, std::allocator<long int> > >; typename std::_Vector_base<_Tp, _Alloc>::pointer = long int*; std::vector<_Tp, _Alloc>::value_type = long int]
vector<_Tp, _Alloc>::
^
note: no known conversion for argument 1 from 'uint32_t {aka long unsigned int}' to 'std::vector<long int, std::allocator<long int> >::iterator {aka __gnu_cxx::__normal_iterator<long int*, std::vector<long int, std::allocator<long int> > >}'
If I modify the declaration of the List<T> class so as to remove the IList<T> and ICollection<T> abstract classes, no errors are generated.
I'm guessing that the way in which I'm using the templated base classes is not correct in this instance.
This issue is not directly related to templates.
class A {
public void f() {}
};
class B : public A {};
class C : public A, public B {};
int main() {
C c;
c.f(); // Error: ambiguous base class!
}
When you inherit a class, the derived class contains an object for the base class, called a base class subobject. So in my example, every B contains an A. And every C contains an A and a B. The problem is, when I try to call f as a member of C, the compiler needs to find the A subobject to call it on. But there are two subobjects with that type! One is directly inherited by C, and the other is inside the inherited B subobject. So the compiler can't figure out what I mean.
The solution in this case is to just not inherit a class twice. In my example, C doesn't need to directly inherit A, since inheriting B will provide it with an indirect A subobject and access to all its members.
In your case, List<T> doesn't need to inherit ICollection<T> directly. It's enough to just derive from IList<T>.
(In other cases, it can be useful to use "virtual inheritance", which tells the compiler "only create one base class subobject for this type, even if I indirectly inherit it more than once in some derived class". But that might be overkill for your code as it stands.)

Inner scoped enumeration, hash function and unordered_set data member

I've the following problem of which I cannot find a solution.
Of course, it could be that a solution does not exist at all, but I'd like to have a try on SO before to give up.
First of all, a snippet that compiles with no errors:
#include <unordered_set>
#include <memory>
struct S {
enum class E: unsigned int { FOO = 0, BAR };
};
namespace std
{
template<>
struct hash<S::E> {
using argument_type = S::E;
using underlying_type = std::underlying_type<argument_type>::type;
using result_type = std::size_t;
result_type operator()(argument_type const &s) const noexcept {
const underlying_type us = static_cast<underlying_type>(s);
hash<underlying_type> hfn;
return hfn(us);
}
};
}
int main() {
std::unordered_set<S::E> set;
}
With this code in mind, I found myself with the requirement of having the unordered_set as a data member of S or, at least, a derived class. A possible working solution is to add add the following lines once the std namespace has been closed:
struct D: public S {
std::unordered_set<S::E> set;
};
Another possible solution is maybe (I've not tried it) to use an unscoped enumeration. Anyway, the first attempt I made was to modify the definition of the struct S as it follows:
struct S {
enum class E: unsigned int { FOO = 0, BAR };
std::unordered_set<E> set;
};
This ends in an error because (if I've correctly understood the problem) the unordered_set requires the specialized hash function. Anyway, the latter requires S::E to be at least declared, thus it is not enough to swap the two pieces of code.
Here the first part of the error log (for it's very long):
In file included from /usr/include/c++/5/bits/hashtable.h:35:0,
from /usr/include/c++/5/unordered_set:47,
from main.cpp:1:
/usr/include/c++/5/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> >’:
/usr/include/c++/5/type_traits:137:12: required from ‘struct std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > >’
/usr/include/c++/5/type_traits:148:38: required from ‘struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
/usr/include/c++/5/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<S::E>’
main.cpp:6:27: required from here
/usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<S::E>) (const S::E&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
^
In file included from /usr/include/c++/5/bits/move.h:57:0,
from /usr/include/c++/5/bits/stl_pair.h:59,
from /usr/include/c++/5/utility:70,
from /usr/include/c++/5/unordered_set:38,
from main.cpp:1:
/usr/include/c++/5/type_traits: In instantiation of ‘struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’:
/usr/include/c++/5/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<S::E>’
main.cpp:6:27: required from here
/usr/include/c++/5/type_traits:148:38: error: ‘value’ is not a member of ‘std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > >’
: public integral_constant<bool, !_Pp::value>
^
In file included from /usr/include/c++/5/unordered_set:48:0,
from main.cpp:1:
/usr/include/c++/5/bits/unordered_set.h: In instantiation of ‘class std::unordered_set<S::E>’:
main.cpp:6:27: required from here
/usr/include/c++/5/bits/unordered_set.h:95:63: error: ‘value’ is not a member of ‘std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
^
/usr/include/c++/5/bits/unordered_set.h:102:45: error: ‘value’ is not a member of ‘std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
typedef typename _Hashtable::key_type key_type;
Usually, in such a case, I can solve with something like a forward declaration, as the one in the example below:
struct B;
struct A { B *link; };
struct B { A *link; };
Unfortunately, I've not been able to do something similar with the enum embedded in a struct and that's why I started this question. Is it possible to solve it, thus avoid to define the derived class D, or deriving is the only viable solution in this case?
You can't forward declare a nested enum, see this answer.
You can do as ForEveR explained, or you can have your generic enum_hash template regardless of std namespace and use it in your data structure, since you are not forced to use std::hash as the hashing function, eg:
template<typename T>
struct enum_hash {
using argument_type = T;
using underlying_type = typename std::underlying_type<argument_type>::type;
using result_type = std::size_t;
result_type operator()(argument_type const &s) const noexcept {
const underlying_type us = static_cast<underlying_type>(s);
std::hash<underlying_type> hfn;
return hfn(us);
}
static_assert(std::is_enum<T>::value, "T must be an enum!");
};
struct S {
enum class E: unsigned int { FOO = 0, BAR };
std::unordered_set<S::E, enum_hash<S::E>> set;
};
You can just write specialization of hash for all enums and then all would work fine.
namespace std {
template<class E>class hash {
using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type;
public:
size_t operator()(const E&e) const {
return std::hash<typename std::underlying_type<E>::type>()(e);
}
};
};

C++11 decltype: How to declare the type that a pointer points to?

I have the following code:
#include <memory>
int main()
{
int* a = new int(2);
std::unique_ptr<decltype(*a)> p(a);
}
which leads to these error message:
In file included from a.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/memory:81:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/unique_ptr.h:138:14: error: '__test' declared as a pointer to a reference of type 'int &'
static _Tp* __test(...);
^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/unique_ptr.h:146:35: note: in instantiation of member class 'std::unique_ptr<int &,
std::default_delete<int &> >::_Pointer' requested here
typedef std::tuple<typename _Pointer::type, _Dp> __tuple_type;
^
a.cpp:7:35: note: in instantiation of template class 'std::unique_ptr<int &, std::default_delete<int &> >' requested here
std::unique_ptr<decltype(*a)> p(a);
^
In file included from a.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/memory:81:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/unique_ptr.h:227:33: error: 'type name' declared as a pointer to a reference of type 'int &'
is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
^
a.cpp:7:35: note: in instantiation of template class 'std::unique_ptr<int &, std::default_delete<int &> >' requested here
std::unique_ptr<decltype(*a)> p(a);
^
2 errors generated.
I understand the reason is that the unique_ptr template expects type int, but decltype(*a) gives int&. In the case that int is a very long and complicated type, how can I make this code work with decltype?
Use std::decay_t. This is the conversion that is applied when you pass an argument to a function by value.
You can use a typedef inside a templated class and then use template specialisation, like this
template<typename T> struct unref {
typedef T raw;
};
template<typename T> struct unref<T&> {
typedef T raw;
};
int main() {
int* a = new int(2);
std::unique_ptr<unref<decltype(*a)>::raw> p(a);
}

Error while using boost::shared_ptr

I'm learning boost and smart pointers. During compilation I got an error, and I can't figure out what is it about. I don't understand what I am doing wrong. The problem is in constructor:
DefaultCreature(const Creature& def) : def_(def) {}
Here is my code:
#include <iostream>
#include <boost/smart_ptr.hpp>
using namespace std;
using namespace boost;
class Creature;
typedef shared_ptr<Creature> PCreature;
class Creature {
public:
Creature(const string& name) : name_(name) {}
const string& getName() const { return name_; }
private:
string name_;
};
class DefaultCreature {
public:
DefaultCreature(const Creature& def) : def_(def) {}
private:
PCreature def_;
};
int main() {
DefaultCreature factory(Creature("lion"));
return 0;
}
And an error:
exercise1.cpp: In constructor ‘DefaultCreature::DefaultCreature(const Creature&)’:
exercise1.cpp:20:52: error: no matching function for call to ‘boost::shared_ptr<Creature>::shared_ptr(const Creature&)’
exercise1.cpp:20:52: note: candidates are:
In file included from /usr/local/include/boost/shared_ptr.hpp:17:0,
from /usr/local/include/boost/smart_ptr.hpp:21,
from exercise1.cpp:2:
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:472:14: note: template<class Ap> boost::shared_ptr::shared_ptr(Ap, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type)
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:472:14: note: template argument deduction/substitution failed:
/usr/local/include/boost/smart_ptr/shared_ptr.hpp: In substitution of ‘template<class Ap> boost::shared_ptr::shared_ptr(Ap, typename boost::detail::sp_enable_if_auto_ptr<Ap, int>::type) [with Ap = Creature]’:
exercise1.cpp:20:52: required from here
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:472:14: error: no type named ‘type’ in ‘struct boost::detail::sp_enable_if_auto_ptr<Creature, int>’
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:446:14: note: template<class Y> boost::shared_ptr::shared_ptr(std::auto_ptr<_Tp1>&)
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:446:14: note: template argument deduction/substitution failed:
exercise1.cpp:20:52: note: types ‘std::auto_ptr<T>’ and ‘const Creature’ have incompatible cv-qualifiers
(...)
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:339:5: note: boost::shared_ptr<T>::shared_ptr() [with T = Creature]
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:339:5: note: candidate expects 0 arguments, 1 provided
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:328:25: note: boost::shared_ptr<Creature>::shared_ptr(const boost::shared_ptr<Creature>&)
/usr/local/include/boost/smart_ptr/shared_ptr.hpp:328:25: note: no known conversion for argument 1 from ‘const Creature’ to ‘const boost::shared_ptr<Creature>&’
The argument to shared_ptr must be the address of a dynamically allocated object but the code is passing in a reference. Change to, for example:
class DefaultCreature {
public:
DefaultCreature(const Creature& def) : def_(new Creature(def)) {}
private:
PCreature def_;
};
or using boost::make_shared:
class DefaultCreature {
public:
DefaultCreature(const Creature& def) :
def_(boost::make_shared<Creature>(def)) {}
private:
PCreature def_;
};
If the instance of DefaultCreature is the only object that has access to the object being pointed to by def_ then there is no reason for it to be a boost::shared_ptr: use boost::scoped_ptr instead. See What C++ Smart Pointer Implementations are available? for a very useful overview of smart pointers.
However, from the posted code there appears to be no reason to be using pointers of any nature. Just store a Creature instance in DefaultCreature (Creature is copyable and there is no polymorphic requirement, based on the posted code).