Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emplace a pointer to a multimap of shared_ptr's doesn't work

Vector works properly

Header
std::vector<std::shared_ptr<SceneNode>> subnodes_m;

Definition
void CompositeSceneNode::AddChild(SceneNode* subnode_p)
{
    subnodes_m.emplace_back(subnode_p);
}

Multimap doesn't

Header
std::multimap<unsigned int, std::shared_ptr<SceneNode>> subnodes_m;

Definition
void CompositeSceneNode::AddChild(SceneNode* subnode_p, unsigned int layerIndex)
{
    subnodes_m.emplace(layerIndex, subnode_p);
}

I get following error:

error C2664: 'std::pair<_Ty1,_Ty2>::pair(const unsigned int &,const _Ty2 &)' :
cannot convert parameter 2 from 'RendererD3DWrapper::SceneNode *'
to 'const std::shared_ptr<_Ty> &'   

Anybody has a clue?

like image 274
user2032932 Avatar asked Dec 19 '22 11:12

user2032932


1 Answers

You can't construct a std::pair<T1,T2> with arguments of types U and V if there is no implicit conversion of U into T1, and V into T2. In your case, there is no implicit conversion of SceneNode* into std::shared_ptr<SceneNode>.

From the C++ standard:

§ 20.3.2 Class template pair [pairs.pair]

template<class U, class V> constexpr pair(U&& x, V&& y);
  1. Requires: is_constructible<first_type, U&&>::value is true and is_constructible<second_type, V&&>::value is true.

  2. Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y).

  3. Remarks: If U is not implicitly convertible to first_type or V is not implicitly convertible to second_type this constructor shall not participate in overload resolution.

Having said that, you can't initialize a std::pair<T1,T2> like below (as emplace builds in-place a std::pair<key_type, mapped_type> known as value_type of std::multimap):

std::pair<unsigned int, std::shared_ptr<SceneNode>> p( 1, new SceneNode );

because the constructor of std::shared_ptr<T> taking a raw pointer (declared below) is an explicit constructor, hence the error you encounter:

§ 20.9.2.2 Class template shared_ptr [util.smartptr.shared]

[...]

template<class Y> explicit shared_ptr(Y* p);

In C++11 you should either build a std::shared_ptr<T> before calling emplace:

subnodes_m.emplace(layerIndex, std::shared_ptr<SceneNode>(subnode_p));

, or you can forward arguments to the constructors of pair's elements (rather than forwarding them to the constructor of std::pair<T1,T2> itself), with a piecewise construction:

subnodes_m.emplace(std::piecewise_construct
                 , std::forward_as_tuple(layerIndex)
                 , std::forward_as_tuple(subnode_p));

DEMO

Why does it work with std::vector of std::shared_ptr's then?

The std::vector<std::shared_ptr<T>>::emplace_back member function forwards the arguments of emplace_back to the constructor of std::shared_ptr<T>, satisfying the explicit context requirement. In case of a map and a multimap, the emplaced type is a pair which has the constructor that forwards arguments further into its elements disabled if the conversion between argument's and parameter's types of those elements is not implicit (as cited above).

like image 54
Piotr Skotnicki Avatar answered Jan 25 '23 23:01

Piotr Skotnicki