vector 的构造函数的另一个违反直觉的行为
Yet another counterintuitive behaviour of vector's constructor
我希望你找到这个问题和答案 instructional/educational(至少你们中的一些人)。
首先,让我们尝试猜测这个程序的输出:
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<char> delimiters = { ",", ";" };
cout << delimiters[0];
}
尝试回答 2 个问题:
- 这个程序格式正确吗?
- 这个程序的输出是什么?
好吧,正如你们中的一些人可能认为的(这是我最初的想法),这应该会导致编译错误,因此我们将字符串文字而不是字符传递给构造函数,对吧?
错了!
这里我们尝试使用两个字符串文字而不是两个字符来初始化一个向量。
template <class T>vector
的 initializer-list
构造函数在标准中被 [vector.overview]§26.3.11.1 定义为 vector(initializer_list<T>)
。在我们的例子中,vector(initializer_list<char>)
.
字符串文字的类型是“array of n const char
”([lex.string]§5.13.5¶8),因此显然初始化列表构造函数不匹配。
但是这个问题不会导致编译器错误,因为编译器能够找到另一个匹配的构造函数!
[over.match.list]§16.3.1.7¶1把规则解释的很清楚:
“当非聚合 [=46=] 类型 T 的对象被列表初始化时 (...),重载决议分两个阶段选择构造函数:
— 最初,候选函数是 class T 的 initializer-list constructors
,参数列表由作为单个参数的初始化列表组成 [我们已经看到不匹配]。
— 如果找不到可行的初始化列表构造函数,则再次执行重载决策,其中候选函数是 class T 的所有构造函数,参数列表由初始化列表的元素组成 [在我们的例子中,两个字符串文字 "," 和 ";" ]".
回到[vector.overview]§26.3.11.1,我们找到这个候选人:
template <class InputIterator> vector(InputIterator first, InputIterator last)
请注意,InputIterator 的类型与向量中 T 的类型没有 link。因此,即使我们正在初始化一个向量,这两个参数也可以是任意类型。唯一的要求是他们确认 InputIterator 的概念,而 const char[] 恰好做到了。
现在构造函数认为它已将两个迭代器传递给同一序列,但实际上它已将迭代器传递给两个完全不同的序列,“,”和“;”。 [forward.iterators]§27.2.5¶ 说:“前向迭代器的 == 域是相同基础序列上的迭代器的域。”。所以这个程序的结果是未定义的。
所以我们问题的答案是:
- 程序格式错误。
- 程序的输出不够精细。
我希望你找到这个问题和答案 instructional/educational(至少你们中的一些人)。
首先,让我们尝试猜测这个程序的输出:
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<char> delimiters = { ",", ";" };
cout << delimiters[0];
}
尝试回答 2 个问题:
- 这个程序格式正确吗?
- 这个程序的输出是什么?
好吧,正如你们中的一些人可能认为的(这是我最初的想法),这应该会导致编译错误,因此我们将字符串文字而不是字符传递给构造函数,对吧?
错了!
这里我们尝试使用两个字符串文字而不是两个字符来初始化一个向量。
template <class T>vector
的 initializer-list
构造函数在标准中被 [vector.overview]§26.3.11.1 定义为 vector(initializer_list<T>)
。在我们的例子中,vector(initializer_list<char>)
.
字符串文字的类型是“array of n const char
”([lex.string]§5.13.5¶8),因此显然初始化列表构造函数不匹配。
但是这个问题不会导致编译器错误,因为编译器能够找到另一个匹配的构造函数!
[over.match.list]§16.3.1.7¶1把规则解释的很清楚:
“当非聚合 [=46=] 类型 T 的对象被列表初始化时 (...),重载决议分两个阶段选择构造函数:
— 最初,候选函数是 class T 的 initializer-list constructors
,参数列表由作为单个参数的初始化列表组成 [我们已经看到不匹配]。
— 如果找不到可行的初始化列表构造函数,则再次执行重载决策,其中候选函数是 class T 的所有构造函数,参数列表由初始化列表的元素组成 [在我们的例子中,两个字符串文字 "," 和 ";" ]".
回到[vector.overview]§26.3.11.1,我们找到这个候选人:
template <class InputIterator> vector(InputIterator first, InputIterator last)
请注意,InputIterator 的类型与向量中 T 的类型没有 link。因此,即使我们正在初始化一个向量,这两个参数也可以是任意类型。唯一的要求是他们确认 InputIterator 的概念,而 const char[] 恰好做到了。
现在构造函数认为它已将两个迭代器传递给同一序列,但实际上它已将迭代器传递给两个完全不同的序列,“,”和“;”。 [forward.iterators]§27.2.5¶ 说:“前向迭代器的 == 域是相同基础序列上的迭代器的域。”。所以这个程序的结果是未定义的。
所以我们问题的答案是:
- 程序格式错误。
- 程序的输出不够精细。