简体   繁体   中英

convert vector<boost::shared_ptr<Foo> > to vector<Foo*> , is it possible?

I have a function that takes, as an argument, an stl vector of pointers to Foo.

However, I also have objects which are shared pointers to the same class Foo. I would like to be able to call this function feeding this other objects too. Do I have to overload the function? I seem to be unable to dynamic-cast it from vector<shared_ptr<Foo>> to vector<Foo*> .

I know one can convert a shared_ptr to a pointer with the Get() method, but what about this? Any ideas?

Update on the question :

I have implemented the solution suggested below, but now, when declaring all the possible types for the template function, I get:

"explicit instantiation of 'void my_func(std::vector&, std::vector&) [with Ptr_Jet1 = Jet*, Ptr_Jet2 = Jet*]' but no definition available"

and the same thing for all the other combinations (for example, Ptr_Jet1 being shared_ptr instead of Jet*.

On the .cpp file, I have:

template<typename Ptr_Jet1, typename Ptr_Jet2>
void my_func(vector<Ptr_Jet1> vec1, vector<Ptr_Jet2> vec2){
//definition
}

and on the .h file, I have:

typedef boost::shared_ptr<Jet> ptr_jet;

template<typename Ptr_Jet1, typename Ptr_Jet2>
void my_func(vector<Ptr_Jet1> vec1, vector<Ptr_Jet2> vec2);
//
template void my_func(vector<Jet*> vec1, vector<Jet*> vec2);
template void my_func(vector<Jet*> vec1, vector<ptr_jet> vec2);
template void my_func(vector<ptr_jet> vec1, vector<Jet*> vec2);
template void my_func(vector<ptr_jet> vec1, vector<ptr_jet> vec2);

I don't get what's wrong here...

shared_ptr<T> and T* are different types, as such you can't simply cast from vector<shared_ptr<T>> to vector<T*> . If you really need a vector<T*> (meaning you can't change your function as per Barnabas Szabolcs's answer) you need to manually copy the pointer out of the source vector. Since you are using boost anyways, I assume that using Boost.Range is ok. If you can use C++11 this is pretty simple to do:

vector< shared_ptr<Foo> > foo;
auto range = foo | boost::adaptors::transform([](shared_ptr<Foo>& ptr) { return ptr.get(); };
std::vector<Foo*> bar(range.begin(), range.end());

This uses C++11 for the lambda function (which could be easily replaced by a functor/functionptr for c++03 code) and auto to save the range. IIRC the return type of the for the range transformation is unspecified in the documentation of Boost.Range , so hardcoding it might be a bad idea. To get rid of auto in c++03 code you could use boost::copy_range :

struct transformation //using a functor instead of a plain function will enable inlining of the transformation, which is liekly benefitial for performance
{ Foo* operator()(shared_ptr<Foo>& ptr) { return ptr.get();}};

std::vector<Foo*> bar = boost::copy_range<std::vector<Foo*>>(foo | boost::adaptors::transform(transformation()));

Of course you could use the transform_iterator from Boost.Iterators instead, but I find that using Range leads to more readable code.

Of course if you use your vector<Foo*> only as an argument to a function, you can skip writing it into a variable and directly call myFunc(copy_range<std::vector<Foo*>>(...)) , which will likely enable the compiler to skip a few copies.

If you wrote that function, I would suggest templatization , as shared_ptr has operator* just as you would use the raw pointers.

template<typename _Tp_ptr_Foo>
void your_fun(const vector<_Tp_ptr_Foo>& ); // or something alike here

In this situation, templatization does essentially the same as function overloading, but with templatization you avoid duplicating your code.

If you don't have control over the function, you need to convert the whole vector. If it is not more than a 1000 elements and you don't do it more than a couple hundred times, there's not much performance loss to fear about.

Unfortunately you cannot dynamic cast the one to the other as they are not related through inheritance. Although they look alike, vector<T> has nothing to do with vector<U> .

UPDATE:
I agree with Grizzly, template arguments are automatically deduced so you don't need to explicitly write that out. So you can keep calling it your_fun(v) .

The only thing you need to be careful with: if you work with header and code files separately, you need to explicitly instruct the compiler that it should create both of your functions , like this:

//header file:

template<typename _Tp_ptr_Foo>
void your_fun(const vector<_Tp_ptr_Foo>& ); 

template void your_fun(const vector<Foo*>& ); 
template void your_fun(const vector<shared_ptr<Foo> >& ); 

//code file:

template<typename _Tp_ptr_Foo>
void your_fun(const vector<_Tp_ptr_Foo>& )
{
  // implementation
}

UPDATE2: (answering Elelias'es comment)

Your template declaration should look like this:

// header file:

template<typename _Tp1, typename _Tp2, typename _Tp3>
void your_fun(const vector<_Tp1>&, const vector<_Tp2>&, const vector<_Tp3>& ); 

After that, you have 2 options:

  1. You can put the definition into a separate code file. In that case, you'll need 6 explicit template instantiations in the implementation file for the header, one for each combination.

  2. You can put the definition into the header and then you don't need the 6 explicit instantiations. I would rather suggest this in this case. Although it does not separate declaration and implementation, it is not that bad solution. I have seen this approach in serious c++ libraries too, as an example you can have a look at operations.hpp in OpenCV.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM