给定一对传统的“开始”和“结束”迭代器,如何创建与 range-v3 兼容的范围?
假设我正在编写一个接受两个迭代器的通用函数,以便与遗留代码兼容。
struct result;
bool keep_line(const std::string&);
result parse_line(const std::string&);
template <typename InputIt>
std::vector<result> parse_lines(InputIt begin, InputIt end)
{
// This is what I want to do...
auto lines = ranges::make_range_out_of_legacy_iterators(begin, end);
return lines
| ranges::view::filter(keep_line)
| ranges::view::transform(parse_line)
| ranges::to<std::vector<result>>();
}
Run Code Online (Sandbox Code Playgroud) 范围将随着 C++20 标准版本进入 C++。
我的问题:我们是否能够构建(现有)任何范围的标准库容器?更重要的是,具有范围视图?
例如,这会不会:
#include <vector>
#include <iostream>
#include <ranges>
int main() {
auto sq = [](int x) { return x * x; };
std::vector<int> vec { 3, 4, 5 };
std::vector<int> squares { std::ranges::views::transform(vec, sq) };
for(auto i : squares) { std::cout << i << ' '; }
std::cout << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
是一个打印的有效程序9 16 25
?
这与 range-v3 库一起编译,这是值得的。
在 C++20 中,<algorithm>
头文件获得了两种新算法:shift_left()
和shift_right()
. 它们都接受任何 LegacyForwardIterator。对于shift_left()
,指定“以i
从?0
”开始的递增顺序执行移动;对于shift_right()
,指定“如果ForwardIt
满足 LegacyBidirectionalIterator 要求,则按照从i
开始的递减顺序执行移动last - first - n - 1
”。
我可以想到一个相当简单的实现方法shift_left()
:
template <typename ForwardIt>
constexpr inline ForwardIt shift_left(ForwardIt first, ForwardIt last, typename std::iterator_traits<ForwardIt>::difference_type n) {
if (n <= 0) return last;
ForwardIt it = first;
for (; n > 0; --n, ++it) {
if (it == last) return first;
}
return std::move(it, last, first);
} …
Run Code Online (Sandbox Code Playgroud) 由于P0593 为低级对象操作隐式创建对象已被接受,因此现在可以在 C++20 中隐式创建对象。
具体而言,提案中引入的措辞允许某些操作(例如std::malloc
)自动创建和启动某些类型的对象的生命周期,即所谓的隐式生命周期类型,如果此类对象的引入会导致具有其他未定义行为的程序具有定义的行为。见[intro.object]/10。
该草案现在进一步指出,如果可以隐式创建多个此类对象集以提供程序定义的行为,则未指定创建这些对象中的哪一个。(相关句子似乎没有出现在我可以访问的最后一个提案修订版中,R5,但在提交草案中。)
实际上是否有一个程序可以观察到这种隐式创建的对象集的选择?换句话说,是否有一个程序通过这个新规则定义了但未指定的行为,以便可以从输出推断出哪些类型的隐式对象(从多个可能的对象中)被创建?
还是这句话只是为了澄清抽象机器上的程序执行(没有可观察到的影响)?
在 C++20 的最终工作草案(以及下面链接的最新公开可用草案)中,关于如何允许对象替换其他对象,[basic.life] 的措辞已经改变,以便指针、引用和对象的name 自动引用新对象。为此,引入了“透明可替换”的关系。但是,我不确定我是否正确理解了这一点。考虑这个例子:
struct X {int a = 3; float b;};
X x;
new(&x.a) int(5);
x.a == 5 // true without std::launder?
Run Code Online (Sandbox Code Playgroud)
在 C++17 中这当然是正确的,因为旧的 int 既不是 const 对象,也不是具有 const 非静态成员的类。
然而,现在新的透明可替换关系可能不再允许这样做了。在考虑新旧 int 对象的关系时,满足条件(8.1)到(8.4),但条件(8.5)呢?
o1 (旧的 int)和 o2 (新的 int)都是完整的对象(旧的 int 肯定是一个子对象,所以这部分是假的)或者 o1 和 o2 分别是对象 p1 和 p2 的直接子对象,而 p1可以透明地替换为 p2。
new int 是一个完整的对象,因为它是“自己”构造的(我们只放置了一个 new int 而不是一个新的 X)?
或者是否可以认为由于[intro.object]\2的措辞,新 int 是 x 的子对象,就像旧 int 一样,(我高度怀疑这是预期的解释,tbh),因此 x 满足p1 和 p2 …
考虑使用范围库的以下代码(来自 c++20)
#include <iostream>
#include <ranges>
#include <vector>
int main() {
std::vector<int> inputs{1, 2, 3, 4, 5, 6};
auto square_it = [](auto i) {
std::cout << i << std::endl;
return i * 2; };
auto results = inputs | std::views::transform(square_it) | std::views::filter([](auto i){ return i % 3 == 0; });
for(auto r : results) {
// std::cout << r << std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
函数中的cout
insquare
是记录square
范围库何时调用该函数。此代码打印
1
2
3
3
4
5
6
6
Run Code Online (Sandbox Code Playgroud)
问题是,为什么匹配过滤器谓词的值会打印两次? …
为什么这段代码可以在#if 0
块就位的情况下工作,但如果删除它,则会失败并显示一组相当复杂的错误消息?更重要的是,我如何使它与上面非常相似的块得到相同的结果?
#include <ranges>
#include <iterator>
#include <optional>
#include <string_view>
#include <iostream>
#include <algorithm>
template <::std::ranges::view View,
typename Pred>
requires ::std::ranges::input_range<View> &&
::std::ranges::common_range<View> &&
::std::is_object_v<Pred> &&
::std::indirect_unary_predicate<const Pred, ::std::ranges::iterator_t<View>>
class skip_after_view : public ::std::ranges::view_interface<skip_after_view<View, Pred>>
{
public:
skip_after_view() = default;
skip_after_view(View v, Pred p)
: subview_(::std::move(v)), pred_(::std::move(p))
{}
class iterator;
friend class iterator;
auto begin() const {
return iterator{subview_.begin(), subview_.end(), &pred_};
}
auto end() const {
return iterator{subview_.end(), subview_.end(), &pred_};
}
private:
View subview_ = View();
Pred pred_; …
Run Code Online (Sandbox Code Playgroud) 使用 C++20,可以为别名模板生成推导准则(请参阅https://en.cppreference.com/w/cpp/language/class_template_argument_deduction 上的“别名模板的推导”部分)。然而,我无法让它们使用聚合初始化语法。在这种情况下,似乎没有生成别名的扣除指南。
看这个例子:
#include <array>
template <size_t N>
using mytype = std::array<int, N>;
// Deduction guideline ???
int main() {
// mytype error_object = {1, 4, 7}; // ERROR
mytype<3> object = {1, 4, 7}; // OK, but I have to manually specify the size.
return object[0];
}
Run Code Online (Sandbox Code Playgroud)
我曾尝试编写演绎指南,但每次都会出现编译器错误。
template <typename T, typename ... U>
mytype(T, U...) -> mytype<1+sizeof...(U)>; // Compiler error
Run Code Online (Sandbox Code Playgroud)
以及我能想到的任何其他准则。
甚至可以自动推导出数组别名的大小吗?
我正在使用 GCC 10.2
c++ aggregate-initialization template-aliases c++20 deduction-guide
似乎在 C++20 中引入了一种叫做“预期析构函数”的东西。在C++17 [class.dtor] 中:
- 在析构函数的声明中,声明符是以下形式的函数声明符 (11.3.5)
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier(opt) 属性说明符-seq(opt)
在C ++ 20这得到了改变,以这样:
- 其 declarator-id 具有以 ~ 开头的非限定 id 的声明声明了一个预期的析构函数;其声明符应为以下形式的函数声明符 ([dcl.fct])
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier(opt) 属性说明符-seq(opt)
那么这个“潜在的破坏者”是什么?那么标准似乎没有澄清,至少对我来说:
- 在类定义结束时,在该类中声明的预期析构函数之间执行重载解析,并带有空参数列表,以选择该类的析构函数,也称为选定的析构函数。如果重载解析失败,则程序格式错误。
引入“预期析构函数”这个新概念的原因是什么?它甚至意味着什么?它如何更改代码?它允许做什么?
我认为这可能是为了用于模板元编程,或者可能与 SFINAE 有什么关系,但这些只是猜测。
考虑这个代码:
#include <tuple>
#include <type_traits>
#include <iostream>
template <typename T, typename = void> struct is_tuple_like : std::false_type {};
template <typename T> struct is_tuple_like<T, decltype(std::tuple_size_v<T>, void())> : std::true_type {};
int main()
{
std::cout << is_tuple_like<std::string>::value << '\n';
}
Run Code Online (Sandbox Code Playgroud)
在 GCC 10.2 和 MSVC 19.28 上,它会导致硬错误,如下所示:
#include <tuple>
#include <type_traits>
#include <iostream>
template <typename T, typename = void> struct is_tuple_like : std::false_type {};
template <typename T> struct is_tuple_like<T, decltype(std::tuple_size_v<T>, void())> : std::true_type {};
int main()
{
std::cout << is_tuple_like<std::string>::value …
Run Code Online (Sandbox Code Playgroud) c++ ×10
c++20 ×10
range-v3 ×2
algorithm ×1
destructor ×1
g++ ×1
libstdc++ ×1
raii ×1
std-ranges ×1