Work with unique_ptr, vector, and int[]

2019-08-12 05:36发布

问题:

Say that I have 3 variables:

vector<int> vec(3);
int stat[3];
auto dyn = make_unique<int[]>(3);

I can initialize any of these if I know the size is 3:

for(auto i = 0; i < 3; ++i) X[3] = i;

Where X is either, vec, stat, or dyn. But I'd like to be able to do this in a template just by passing in X again. What I'd need in order to do this is:

  1. The contained type
  2. The container size

Can I get that in a function like:

template <typename T>
void init(T& X);

Or am I unable to extract size information from the unique_ptr? Or type in a universal fashion? (I've marked this question C++17 in the hopes that size can be used.)

回答1:

You are not going to be able to get the size from the unique pointer. When you use auto dyn = make_unique<int[]>(3); it gets translated to

make_unique<int[]>(new int[3]())

Which is a pointer and we lose the size information. All the array overload does for unique pointer is change the delete call in the destruction to delete[]. If you want to use an unique_ptr "array" then you wil need to pass the size.



回答2:

You can't do this for unique_ptr<T[]> - that's still just a raw pointer that doesn't have any idea what its size is.

But any other container, you don't even need size(). Just C++11 suffices:

 template <class T>
 void init(T& container) {
     for (auto& elem : container) {
         elem = 3;
     }
 }

You can add SFINAE or a static_assert on top of that to ensure that the elements of T are actually assignable to 3, but otherwise that suffices.



回答3:

If you’re using a loop over the respective contents, then you actually don’t care for the actual containers or holders. This is a good time to be using a pair of iterators, an array view, or a range. E.g.:

template<typename It>
void init(It first, It last)
{
    // or use an algorithm, e.g. std::iota(first, last, 0)
    int i = 0;
    for(; first != last; ++last, ++i) *first = i;
}

// use as:
using std::begin;
using std::end;
init(begin(vec), end(vec));
init(begin(stat), end(stat));
init(dyn.get(), dyn.get() + 3);

Note that you either have to hardcode the size 3 in the std::unique_ptr<int[]> case, or keep track of it yourself somewhere in a variable. The size information is not available otherwise.