std::initializer_list的类型推断

type inference for std::initializer_list

本文关键字:类型 list initializer std      更新时间:2023-10-16

如果我这样写

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")};

来避免与所涉及的模板的类型推导机制相关的问题?

更新:回答你关于类型推断的问题:vector<string>的初始化列表构造函数接受一个initializer_list<string>。它没有模板化,所以在类型推断方面没有发生任何事情。

尽管如此,这里应用的类型转换和重载解析规则还是令人感兴趣的,所以我将保留我最初的答案,因为您已经接受了它:

原始答:

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

然后尝试找到vector<string>的合适构造函数来匹配该列表。如何做到这一点是一个有点复杂的过程,如果你对确切的过程感兴趣,你最好查阅一下标准本身。

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

另一个可能更有趣的例子:

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

这些是做什么的?

  1. 第一行比较简单。初始化列表 {3}可以转换为类似于上面{"onm", "two", "three"}示例的std::initializer_list<long>,因此您得到一个具有单个元素的向量,其值为3。

  2. 第二行不同。它构造了一个包含3个空字符串的向量。为什么?因为初始化列表 {3}无法转换为std::initializer_list<string>,所以"普通"构造函数std::vector<T>::vector(size_t, T = T())开始并给出三个默认构造的字符串。

  3. 这个应该和第二个差不多,对吧?它应该给出一个空向量,换句话说,没有默认构造的字符串。错! 0可以被视为空指针常量,并验证std::initializer_list<string>。只是这次列表中的单个字符串是由空指针构造的,这是不允许的,所以你会得到一个异常。

没有类型推断,因为vector只提供了一个带初始化列表的完全专门化的构造函数。我们可以间接添加一个模板来进行类型演绎。下面的示例显示了std::initializer_list<const char*>是vector构造函数的无效实参。

#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" } ); 
}

现场演示

From 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