Question

If I write this

std::vector<std::string> v{"one","two","three"};

What is the type inferred to the associated std::initializer_list template ? In other words, when the char * string literals are converted to std::string ?

It's a better idea to declare it as

std::vector<std::string> v{std::string("one"),
                           std::string("two"),
                           std::string("three")};

to avoid issues connected to the type-deduction mechanism of the templates involved ? I'll keep the same optimizations with this ?

Was it helpful?

Solution

Update: To answer your question about type inference: The initializer list constructor of vector<string> takes an initializer_list<string>. It is not templated, so nothing happens in terms of type inference.

Still, the type conversion and overload resolution rules applied here are of some interest, so I'll let my initial answer stand, since you have accepted it already:

Original answer:

At first, the compiler only sees the initializer list {"one","two","three"}, which is only a list of initializers, not yet an object of the type std::initializer_list.

Then it tries to find an appropiate constructor of vector<string> to match that list. How it does that is a somewhat complicated process you would do best to look up in the standard itself if you are interested in the exact process.

Therefore, the compiler decides to create an actual object of std::initializer_list<string> from the initializer list, since the implicit conversion from the char*'s to std::strings makes that possible.

Another, maybe more interesting example:

std::vector<long>   vl1{3};
std::vector<string> vs1{3};
std::vector<string> vs2{0};

What do these do?

  1. The first line is relatively easy. The initializer list {3} can be converted into a std::initializer_list<long> analogous to the {"onm", "two", "three"} example above, so you get a vector with a single element, which has value 3.

  2. The second line is different. It constructs a vector of 3 empty strings. Why? Because an initializer list {3} can by no means be converted into an std::initializer_list<string>, so the "normal" constructor std::vector<T>::vector(size_t, T = T()) kicks in and gives three default-constructed strings.

  3. Well this one should be roughly the same as the second, right? It should give an empty vector, in other words, with zero default-constructed strings. WRONG!. The 0 can be treated as a nullpointer constant, and validates the std::initializer_list<string>. Only this time the single string in that list gets constructed by a nullpointer, which is not allowed, so you get an exception.

OTHER TIPS

There is no type inference because vector provide only a fully specialized constructor with the initializer list. We could add a template indirection to play with type deduction. The example below show that a std::initializer_list<const char*> is an invalid argument to the vector constructor.

#include <string>
#include <vector>

std::string operator"" _s( const char* s, size_t sz ) { return {s, s+sz}; }

template<typename T>
std::vector<std::string> make_vector( std::initializer_list<T> il ) {
    return {il};
}

int main() {
    auto compile = make_vector<std::string>( { "uie","uieui","ueueuieuie" } ); 
    auto compile_too = make_vector<std::string>( { "uie"_s, "uieui", "ueueuieuie" } ); 
    //auto do_not_compile = make_vector( { "uie","uieui","ueueuieuie" } ); 
}

Live demo

From http://en.cppreference.com/w/cpp/language/string_literal:

The type of an unprefixed string literal is const char[]

Thus things go this way:

#include <iostream>
#include <initializer_list>
#include <vector>
#include <typeinfo>
#include <type_traits>
using namespace std;

int main() {
    std::cout << std::boolalpha;
    std::initializer_list<char*> v = {"one","two","three"}; // Takes string literal pointers (char*)
    auto var = v.begin();
    char *myvar;
    cout << (typeid(decltype(*var)) == typeid(decltype(myvar))); // true

    std::string ea = "hello";
    std::initializer_list<std::string> v2 = {"one","two","three"}; // Constructs 3 std::string objects
    auto var2 = v2.begin();
    cout << (typeid(decltype(*var2)) == typeid(decltype(ea))); // true
    std::vector<std::string> vec(v2);
    return 0;
}

http://ideone.com/UJ4a0i

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top