根据类型,“空”标准范围视图的行为不一致
Inconsistent behavior with `empty` std ranges view depending on type
考虑以下代码片段:
#include <vector>
#include <ranges>
#include <iostream>
struct A
{
};
struct B
{
B(void *) {};
};
template<class T, class R>
std::vector<T> foo(R &&range)
{
return std::vector<T> {
std::ranges::begin(range),
std::ranges::end(range)
};
}
int main()
{
std::cout << foo<A>(std::views::empty<A>).size() << "\n";
std::cout << foo<B>(std::views::empty<B>).size() << "\n";
}
在 GCC 11.2 (https://godbolt.org/z/s6aoTGbr4) 和 MSVC 19.30.30705 (Visual Studio 2022 17.0).
显然在第二种情况下 std::views::empty
视图生成迭代器,导致构造函数选择初始化列表。
将 std::vector<T> { ... }
更改为 std::vector<T> ( ... )
可以解决此问题,但我想知道它是否真的是 empty
视图的实现(甚至定义)中的错误?
这就是您应该警惕列表初始化的原因!特别是这种语法:
std::vector<T>{ ... }
当您专门尝试调用 std::initializer_list<T>
构造函数并专门为 vector
提供元素时, 应该 仅 (!!)而不是在任何其他情况下。就像这一个,您不会尝试调用该构造函数。
问题是在列表初始化中,强烈推荐 std::initializer_list
构造函数。如果它完全可行,则首先选择它。在这种情况下,我们试图从两个指向 B
的指针构造一个 vector<B>
(这是 empty<B>
的迭代器类型)。 B
可以从 B*
构造(通过 void*
构造函数),这使得 std::initializer_list<B>
构造函数成为可行的候选者,这意味着它是 selected.
因此,您得到一个 vector<B>
和两个 B
,每个都由一个空指针构成。
对于 A
,没有这样可行的构造函数,因此您回退到正常初始化,这将 select 迭代器对构造函数,给您一个空的 vector<A>
.
此处的解决方法是在初始化时使用 ()
s,因为这实际上是您打算做的。
考虑以下代码片段:
#include <vector>
#include <ranges>
#include <iostream>
struct A
{
};
struct B
{
B(void *) {};
};
template<class T, class R>
std::vector<T> foo(R &&range)
{
return std::vector<T> {
std::ranges::begin(range),
std::ranges::end(range)
};
}
int main()
{
std::cout << foo<A>(std::views::empty<A>).size() << "\n";
std::cout << foo<B>(std::views::empty<B>).size() << "\n";
}
在 GCC 11.2 (https://godbolt.org/z/s6aoTGbr4) 和 MSVC 19.30.30705 (Visual Studio 2022 17.0).
显然在第二种情况下 std::views::empty
视图生成迭代器,导致构造函数选择初始化列表。
将 std::vector<T> { ... }
更改为 std::vector<T> ( ... )
可以解决此问题,但我想知道它是否真的是 empty
视图的实现(甚至定义)中的错误?
这就是您应该警惕列表初始化的原因!特别是这种语法:
std::vector<T>{ ... }
当您专门尝试调用 std::initializer_list<T>
构造函数并专门为 vector
提供元素时,应该 仅 (!!)而不是在任何其他情况下。就像这一个,您不会尝试调用该构造函数。
问题是在列表初始化中,强烈推荐 std::initializer_list
构造函数。如果它完全可行,则首先选择它。在这种情况下,我们试图从两个指向 B
的指针构造一个 vector<B>
(这是 empty<B>
的迭代器类型)。 B
可以从 B*
构造(通过 void*
构造函数),这使得 std::initializer_list<B>
构造函数成为可行的候选者,这意味着它是 selected.
因此,您得到一个 vector<B>
和两个 B
,每个都由一个空指针构成。
对于 A
,没有这样可行的构造函数,因此您回退到正常初始化,这将 select 迭代器对构造函数,给您一个空的 vector<A>
.
此处的解决方法是在初始化时使用 ()
s,因为这实际上是您打算做的。