std :: initializer_list的类型推断

时间:2014-03-28 13:56:27

标签: c++ templates c++11 type-inference initializer-list

如果我写这个

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

相关std::initializer_list模板的推断类型是什么? 换句话说,当char *字符串文字转换为std::string

将其声明为

是一个更好的主意
std::vector<std::string> v{std::string("one"),
                           std::string("two"),
                           std::string("three")};

避免与所涉及模板的类型推导机制相关的问题? 我会对此保持相同的优化吗?

3 个答案:

答案 0 :(得分:7)

更新:要回答有关推理类型的问题: vector<string>的初始化列表构造函数需要initializer_list<string>。它没有模板化,所以在类型推断方面没有任何反应。

但是,这里应用的类型转换和重载解析规则是有意义的,所以我会让我的初步答案成立,因为你已经接受了它:

原始回答:

首先,编译器只能看到初始化列表 {"one","two","three"},它只是一个初始化列表,而不是std::initializer_list类型的对象。

然后它尝试找到vector<string>的适当构造函数来匹配该列表。 如果您对确切的过程感兴趣,那么如果这样做有点复杂,您最好在标准中查找。

因此,编译器决定从初始化列表创建std::initializer_list<string>的实际对象,因为从char*std::string的隐式转换s使这成为可能。

另一个,也许更有趣的例子:

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

这些是做什么的?

  1. 第一行相对容易。 初始值设定项列表 {3}可以转换为类似于上面std::initializer_list<long>示例的{"onm", "two", "three"},因此您可以获得具有单个元素的向量,其值为3

  2. 第二行不同。它构造了一个3个空字符串的向量。为什么?因为初始化列表 {3}决不能转换为std::initializer_list<string>,所以“普通”构造函数std::vector<T>::vector(size_t, T = T())会启动并提供三个默认构造的字符串

  3. 那么这个应该与第二个大致相同,对吗?它应该给出一个空向量,换句话说,使用零默认构造的字符串。的 WRONG!即可。可以将0视为空指针常量,并验证std::initializer_list<string>。只有这次该列表中的单个字符串由nullpointer构造,这是不允许的,所以你得到一个例外。

答案 1 :(得分:3)

没有类型推断,因为向量仅提供具有初始化列表的完全专用的构造函数。我们可以添加模板间接来玩类型推导。下面的示例显示std::initializer_list<const char*>是向量构造函数的无效参数。

#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

答案 2 :(得分:2)

来自http://en.cppreference.com/w/cpp/language/string_literal

  

未加前缀的字符串文字的类型为const char[]

因此事情就是这样:

#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