Search code examples
c++vectorc++17ctad

why does CTAD result in a vector with iterators, not integers?


#include <boost/type_index.hpp>
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> initv { 42, 31, 7 };

    std::vector      v1{initv.begin(), initv.end()};  // CTAD
    std::vector<int> v2{initv.begin(), initv.end()};

    std::cout << boost::typeindex::type_id_with_cvr<decltype(v1)>().pretty_name() << std::endl;
    std::cout << boost::typeindex::type_id_with_cvr<decltype(v2)>().pretty_name() << std::endl;
}

output:

std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, std::allocator<__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > > > >
std::vector<int, std::allocator<int> >

CTAD produces a vector with iterators, not integers.

Is this correct, given that std::vector has a constructor that takes iterators ?


Solution

  • This is because of how you initialize the vector. With

    std::vector      v1{initv.begin(), initv.end()};
    

    You are doing direct-list-initialization which uses the std::initializer_list constructor and the list contains 2 std::vector<int>::iterators. This is why it deduces for iterators.

    If you switch to using parentheses instead like

    std::vector      v1(initv.begin(), initv.end());
    

    then you are no longer using the std::initializer_list constructor and instead using the iterator range constructor which will correctly deduce the value_type of the iterators.


    To summerize, when you use {} you are signaling you are using a list of initializers, so that is what is used if it is available.

    If you would like to take a deep dive down the rabbit hole that is initialization in C++, I strongly recommend watching Nicolai Josuttis CPPCON talk “The Nightmare of Initialization in C++”