【问题标题】:How does the compiler decide between vector vs initializer_list?编译器如何在 vector 和 initializer_list 之间做出决定?
【发布时间】:2017-07-25 14:47:54
【问题描述】:

在下面的代码中,两个版本的 print 方法都存在,第一个调用解析为带有 initializer_list 的调用。如果我用 initializer_list 注释掉定义,程序就会无缝地使用向量版本。在第一种情况下,我希望编译器会抱怨!

#include <iostream>
#include <vector>
using namespace std;

void print(const vector<int>& v1){
        cout << "vector \n";
}
void print(const initializer_list<int>& il) {
         cout << "init list \n";
}

int main() {
        print({1,2,3,4,5});
        return 0;
}

【问题讨论】:

  • 这就是 C++ 中重载解析的工作原理。

标签: c++ c++11 vector initialization


【解决方案1】:

这就是overload resolution 在 C++ 中的工作方式。 print 的两个版本都可以解决重载问题。

  • print(const vector&lt;int&gt;&amp; v1) 是一个可行的重载解析函数,因为调用者中的输入初始化列表 {1,2,3,4,5} 可以隐式转换为 std::vector&lt;int&gt;。
  • print(const initializer_list&lt;int&gt;&amp; il) 是一个可行的重载解析函数,因为调用者中的输入类型完全匹配。

当两个重载都起作用时,print(const initializer_list&lt;int&gt;&amp; il) 被选为最佳可行函数,因为它是完美匹配,并且完美匹配在重载解决方案中的优先级高于隐式转换。

【讨论】:

    【解决方案2】:

    重载解析的工作原理是将参数转换为参数类型所需的操作表示为转换序列,然后根据一些规则对转换序列进行排序。

    由于我们正在初始化引用,因此 [over.ics.ref]/2 适用:

    当引用类型的参数没有直接绑定到参数表达式时,转换顺序是根据 13.3.3.1 将参数表达式转换为引用的基础类型所需的顺序。从概念上讲,此转换序列对应于使用参数表达式复制初始化基础类型的临时变量。顶级 cv 限定的任何差异都包含在初始化本身中,不构成转换。

    所以这允许我们使用与代码相同的转换顺序规则:

    void o_print(initializer_list<int> ol);
    void o_print(vector<int> o1);
    

    即使用非 const 对象作为参数而不是 const 引用。

    现在:

    • 根据 [over.ics.list]/2,将花括号列表 int 转换为 std::initializer_list&lt;int&gt; 是一种身份转换。 (如果列表成员需要提升或转换为 int,则不需要)。

    • 根据[over.ics.list]/4,将花括号列表转换为非聚合类(此处为std::vector&lt;int&gt;)通过重载决议选择std::vector的构造函数,并定义此序列作为用户定义的转换序列。

    现在我们终于准备好应用排名规则了。身份转换的排名高于用户定义的转换序列,因此initializer_list&lt;int&gt; 版本获胜。

    注意。所有引用均指向 C++14。

    【讨论】:

      猜你喜欢
      • 2011-05-19
      • 2013-12-30
      • 2021-01-11
      • 1970-01-01
      • 2017-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-23
      相关资源
      最近更新 更多