25

I read about deduction guides for std::vector from using cppreference.

Example:

#include <vector>

int main() {
   std::vector<int> v = {1, 2, 3, 4};
   std::vector x{v.begin(), v.end()}; // uses explicit deduction guide
}

So, I have some questions about it:

  • What are std::vector deduction guides in C++17?

  • Why and when do we need vector deduction?

  • Here, Is x a std::vector<int> or a std::vector<std::vector<int>>?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
msc
  • 33,420
  • 29
  • 119
  • 214
  • 3
    `x` is probably a `std::vector::iterator>`. – Henri Menke Sep 01 '17 at 08:16
  • Try to add value to x to see: [Demo](http://coliru.stacked-crooked.com/a/640910c60c5735c1) so `std::vector::iterator>`. – Jarod42 Sep 01 '17 at 08:24
  • Do you know what deduction guides are in general? If not, [this](http://en.cppreference.com/w/cpp/language/class_template_argument_deduction#User-defined_deduction_guides) could be useful. – HolyBlackCat Sep 01 '17 at 08:26
  • 1
    @gsamaras: I mean that by testing `x.push_back(42);`, `x.push_back(v.begin());` and `x.push_back(v);` (correct) compilers (with correct library) would tell the error/valid cases (and moreover error case should probably give the correct type). – Jarod42 Sep 01 '17 at 08:52

3 Answers3

21

What are std::vector deduction guides in C++17?

An user-defined deduction guide allows users to decide how class template argument deduction deduces arguments for a template class from its constructor arguments. In this case, it seems that std::vector has an explicit guide that should make construction from an iterator pair more intuitive.


Why and when do we need vector deduction?

We don't "need" it, but it is useful in generic code and in code that's very obvious (i.e. code where explicitly specifying the template arguments is not beneficial to the reader).


Is x a vector<int> or a vector<vector<int>>?

Here's a nice trick to figure this out quickly - write a template function declaration without a definition and attempt to call it. The compiler will print out the type of the passed arguments. Here's what g++ 8 prints out:

template <typename> 
void foo();

// ...

foo(x);

error: no matching function for call to foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ...

As you can see from the error message, x is deduced to std::vector<std::vector<int>::iterator>.


Why?

std::vector's deduction guides are available on cppreference.org. The Standard seems to define an explicit deduction guide from an iterator pair:

enter image description here

The behavior encountered in g++ 8 seems to be correct regardless, as (quoting Rakete1111)

  • overload resolution prefers the constructor with std::initializer_list with the braced initializer list

  • other constructors are considered only after all std::initializer_list constructors have been tried in list-initialization

std:vector<std::vector<int>::iterator> is therefore the correct result when using list-initialization. live example

When constructing x with std::vector x(v.begin(), v.end()), int will be deduced instead. live example

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 4
    Your last point is only half true. Even with the missing deduction guide, the type will be `std:vector::iterator>` because overload resolution prefers the constructor with `std::initializer_list` with the braced initializer list. – Rakete1111 Sep 01 '17 at 09:03
  • @Rakete1111 That's not what the example on cppreference suggests - I don't know if the example is correct, but user-defined deduction guide have priority over automatic deduction guide in some cases, which could make the deduction correct here. – Holt Sep 01 '17 at 09:50
  • @Rakete1111 According to [Best viable function](http://en.cppreference.com/w/cpp/language/overload_resolution#Best_viable_function), "6) or, if not that, F1 is generated from a user-defined deduction-guide and F2 is not", I think it means the constructor with deduction guide is preferred in overload resolution. – songyuanyao Sep 01 '17 at 09:53
  • 2
    @songyuanyao Yes I think so too, but isn't it the case that the other constructors are considered only *after* all `std::initializer_list` constructors have been tried in [list-initialization](http://en.cppreference.com/w/cpp/language/overload_resolution#List-initialization)? So, the iterator pair constructor is not even considered I think. – Rakete1111 Sep 01 '17 at 10:03
  • 4
    What @Rakete1111 said. `std:vector::iterator>` is the correct result. – T.C. Sep 01 '17 at 10:04
  • 5
    And that cppreference example is now fixed. – T.C. Sep 01 '17 at 10:10
  • I'm not sure I like that trick; you conveniently omitted the `&` in the error message (which signifies the value category). It doesn't matter in this case because you know what `x`'s type should roughly look like, but can be quite confusing if it can actually be a reference type. – T.C. Sep 01 '17 at 10:32
  • Thanks for the comments - I'll admit I find it surprising that `std::initializer_list` is preferred over an explicit deduction guide like in the example, and I'm glad that the cppreference example was changed (as it was misleading). I've improved my answer. – Vittorio Romeo Sep 01 '17 at 12:55
7

Here, Is x a std::vector<int> or a std::vector<std::vector<int>>?

The other answers here address your other questions, but I wanted to address this one a little more thoroughly. When we're doing class template argument deduction, we synthesize a bunch of function templates from the constructors, and then some more from deduction guides and perform overload resolution to determine the correct template parameters.

There are quite a few constructors to std::vector<T,A> , but most of them don't mention T which would make T a non-deduced context and thus not a viable option in this overload. If we pre-prune the set to only use the ones that could be viable:

template <class T> vector<T> __f(size_t, T const& );    // #2
template <class T> vector<T> __f(vector<T> const& );    // #5
template <class T> vector<T> __f(vector<T>&& );         // #6, NB this is an rvalue ref
template <class T> vector<T> __f(initializer_list<T> ); // #8

And then also this deduction guide, which I'll also simplify by dropping the allocator:

template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );

Those are our 5 candidates, and we're overloading as if by [dcl.init], a call via __f({v.begin(), v.end()}). Because this is list-initialization, we start with the initializer_list candidates and, only if there aren't any, do we proceed to the other candidates. In this case, there is an initializer_list candidate that is viable (#8), so we select it without even considering any of the others. That candidate deduces T as std::vector<int>::iterator, so we then restart the process of overload resolution to select a constructor for std::vector<std::vector<int>::iterator> list-initialized with two iterators.

This is probably not the desired outcome - we probably wanted a vector<int>. The solution there is simple: use ()s:

std::vector x(v.begin(), v.end()); // uses explicit deduction guide

Now, we're not doing list-initialization so the initializer_list candidate isn't a viable candidate. As a result, we deduce vector<int> through the deduction guide (the only viable candidate), and end up calling the iterator-pair constructor of it. This has the side benefit of actually making the comment correct.


This is one of the many places where initializing with {} does something wildly different than initializing with (). Some argue that {} is uniform initialization - which examples like this seem to counter. My rule of thumb: use {} when you specifically, consciously need the behavior that {} provides. () otherwise.

Barry
  • 286,269
  • 29
  • 621
  • 977
5

What are std::vector deduction guides in C++17?

Class template argument deduction specifies: "In order to instantiate a class template, every template argument must be known, but not every template argument has to be specified."

And that's localized for std:vector, I mean a std:vector is just a class. Nothing special about it.

Here is the std::vector deducation guide from the ref:

template< class InputIt,
          class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
vector(InputIt, InputIt, Alloc = Alloc())
  -> vector<typename std::iterator_traits<InputIt>::value_type, Alloc>;

If you are unfamiliar with the syntax, please read What are template deduction guides in C++17?

Why and when do we need vector deduction?

You need guides when the deduction of the type from the arguments is not based on the type of one of those arguments.

Is x a vector<int> or a vector<vector<int>>?

Neither!

It's an:

std::vector<std::vector<int>::iterator>

Forcing a simple compilation error (by assigning a number to x for example) will unveil its type):

error: no match for 'operator=' (operand types are 'std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, std::allocator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > > >' and 'int')

PS:

Why do we need that initialization, Alloc = Alloc() in that guide?

That's a default argument, which allows to pass in an allocator. The defaulting means you don't need two guides.

gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 2
    Why do we need that initialization, `Alloc = Alloc()` in that guide? Isn't `Alloc` just good enough to represent the type itself? – Dean Seo Sep 01 '17 at 09:05
  • @DeanSeo It's a default argument. – Barry Sep 01 '17 at 13:04
  • @Barry Right. But what we need from a deduction guide is type, not arguments? The compiler already knows its _default_ type without *Alloc()* it seems? – Dean Seo Sep 01 '17 at 13:51
  • @Dean ... because it's a default argument. This guide also handles the case where you pass in an allocator. The defaulting means you don't need two guides. – Barry Sep 01 '17 at 14:06
  • I agree with @Barry, answer updated (although I am not sure if I should do that). Thanks for the upvote! – gsamaras Sep 01 '17 at 16:24
  • @Barry Ah I see, just like a function template. I guess I am not used to the syntax of the deduction guide. Thanks. :) – Dean Seo Sep 01 '17 at 16:57