Search code examples
c++templatesreturn-type

create a to_vector function that can conditionally const cast to elements of input range based on output type


I would like to create a to_vector function that works with input vieweable_ranges. I can easily get this to work if input view and output vector have exactly the same type, but cannot get it to work if the output requires a const cast on the elements of the input range. In my case, the input ranges have non-const pointers, but the output may specify const pointers. I want to use it something like this:

auto a = to_vector( view );  // returns a vector<obj *>. This case is easy
vector<const obj *> b = to_vector( view ); // returns a vector of const pointers. This gives error shown at the end

The standard library has no problem casting pointers when making the vector

vector<const obj *> c( view.begin(), view.end() ); 

But I can't create a templated function to make it work. I've tried many variations, but I think my closest idea is something like this:

#include <ranges>
#include <vector>

using namespace std::ranges;
template <range Range, typename T = range_value_t<Range>> // default element type, T, based on input range
inline std::vector<T> to_vector(Range&& r) { // idea: user specifies T. (doesn't work)
    std::vector<T> v;

    if constexpr (std::ranges::sized_range<T>) {
        v.reserve(std::ranges::size(r));
    }

    std::copy(std::ranges::begin(r), std::ranges::end(r), std::back_inserter(v));
    return v;
}

The to_vector compiles, but I get an error when I try to require a vector output with an error something like this:

cannot convert std::vector<Obj *,std::allocator<Obj *>>' to 'std::vector<const Obj *,std::allocator<const Obj *>>


Solution

  • This is because your function creates a return value of type std::vector<Obj*>, which is indeed a different type to std::vector<const Obj*>; and so unless std::vector provided an overloaded constructor which accepted a non-const version of itself then this conversion is impossible. The return value is not deduced by the value you assign the result of the function to but by the T template parameter, so this will work if you explicitly call the function with T=const Obj*

    You would need to explicitly overload the function to construct a vector of const objects, either by passing a dummy tag parameter, an extra template parameter or a to_const_vector function.