簡體   English   中英

std::ranges::to 是否允許轉換為 std::map?

[英]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並不是真的打算被允許?

std::map 到 std::vector:

根據[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)...)

其中CvectorRmap<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_typepair<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 的一個實現錯誤,因為如果vectorreserve() function 不存在它會編譯,而且這個優化的重載似乎沒有限制value_type必須是可復制分配的。

std::vector 到 std::map:

對於以下

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM