[英]Does std::ranges::to allow converting to a std::map?
在std::ranges::to
paper wg21.link/p1206 中,概述部分具有以下內容
//Supports converting associative container to sequence containers
auto f = ranges::to<vector>(m);
但是我找不到在論文的 rest 中描述轉換為std::map
的詳細信息。 我在https://github.com/TartanLlama/ranges中嘗試了 range-v3 和 Sy Brand 的ranges::to
實現,它們都沒有編譯將范圍轉換為std::map
的代碼。 那么這只是這些庫中缺少的,還是正在轉換為std::map
並不是真的打算被允許?
那么這只是這些庫中缺少的,還是正在轉換為
std::map
並不是真的打算被允許?
根據[range.utility.conv.to]的描述,這個
map<int, double> m;
auto f = ranges::to<vector>(m);
將調用以下重載
template<template<class...> class C, input_range R, class... Args> constexpr auto to(R&& r, Args&&... args);
讓
DEDUCE_EXPR
定義如下:
C(declval<R>(), declval<Args>()...)
如果這是一個有效的表達式,- 否則,
C(from_range, declval<R>(), declval<Args>()...)
如果這是一個有效的表達式,- 否則,
C(declval<input-iterator>(), declval<input-iterator>(), declval<Args>()...)
如果這是一個有效的表達式,- 否則,程序是病式的。
返回:
to<decltype(DEDUCE_EXPR)>(std::forward<R>(r), std::forward<Args>(args)...)
。
其中C
是vector
, R
是map<int, double>&
, Args...
是空參數包。 請注意,C++23 為vector
引入了以下范圍版本構造函數
template<container-compatible-range<T> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());
效果:使用指定的分配器構造一個向量 object,其元素在
rg
范圍內。
和以下 CTAD
template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>>
vector(from_range_t, R&&, Allocator = Allocator())
-> vector<ranges::range_value_t<R>, Allocator>;
所以C(from_range, declval<R>())
是一個有效的表達式, DEDUCE_EXPR
的類型將是vector<pair<const int, double>>
,這將進一步調用
to<vector<pair<const int, double>>>(m);
它將通過范圍版本構造函數構造一個value_type
為pair<const int, double>
的vector
。
所以 C++23 中的ranges::to<vector>(m)
基本上等同於
map<int, double> m;
vector f(m.begin(), m.end());
range-v3失敗的原因是它的內部實現檢測到vector<pair<const int, double>>
是可預留的,所以它會先默認構造vector
並調用v.reserve()
預分配memory,然后通過調用v.assign()
復制map
,但由於pair<const int, double>
不可復制賦值,因此編譯失敗。
我懷疑這是 range-v3 的一個實現錯誤,因為如果vector
的reserve()
function 不存在它會編譯,而且這個優化的重載似乎沒有限制value_type
必須是可復制分配的。
對於以下
auto g = ranges::to<map>(f);
由於std::map
在 C++23 中也有以下構造函數和相應的 CATD
template<container-compatible-range<value_type> R>
map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
所以按照相同的游戲規則,我們將得到一個map<int, double>
。
std::ranges::to
是否允許轉換為std::map
?
是的。
我在https://github.com/TartanLlama/ranges中嘗試了 range-v3 和 Sy Brand 的
ranges::to
實現,它們都沒有編譯將范圍轉換為std::map
的代碼
我沒有嘗試過 Sy 的實現,看起來 range-v3 的實現被奇怪地破壞了:
#include <map>
#include <vector>
#include <range/v3/range/conversion.hpp>
int main() {
std::vector<std::pair<int, int>> v = {{1, 2}, {3, 4}};
// this works (explicit)
// m1 is a std::map<int, int>
auto m1 = ranges::to<std::map<int, int>>(v);
// this works (deduced with a pipe)
// m2 is a std::map<int, int>
auto m2 = v | ranges::to<std::map>();
// but this does not (deduced, direct call)
auto m3 = ranges::to<std::map>(v);
}
問題是 range-v3 中的 class 模板直接調用版本出於某種原因專門嘗試實例化C<range_value_t<R>>
(在這種情況下為std::map<std::pair<int, int>>
,顯然是錯誤的)即使這里已經有一個元函數可以做正確的事情並且會推斷出std::map<int, int>
(由 pipe 版本使用)。
標准庫中的ranges::to
指定這兩個做同樣(正確)的事情,所以這將在 C++23 中工作。 這只是 range-v3 中的一個簡單錯誤修復。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.