c++ 无法推导范围迭代器的类型

t3psigkw  于 2023-02-01  发布在  其他
关注(0)|答案(1)|浏览(128)

以下代码无法在MSVC 2022和gcc 12.1(启用c++20)上编译,并出现相同的错误:

#include <iostream>
#include <random>
#include <ranges>
#include <vector>

using namespace std;

int main()
{
    auto v0 = views::iota(0) | views::take(5) | views::transform([](auto i) -> double { return 10.1 * double(i); });
    // output: 0 10.1 20.2 30.3 40.4
    // output: double
    for (auto i = v0.begin(); i != v0.end(); ++i) 
        cout << *i << ' '; 
    cout << endl << typeid(decltype(*v0.begin())).name() << endl;

    // error: template argument deduction failed
    discrete_distribution<int>::param_type p0(v0.begin(), v0.end()); // (**)
    
    // this is ok
    vector<double> v1{10.1, 20.2, 30.3};
    discrete_distribution<int>::param_type p1(v1.begin(), v1.end());
    return 0;
}

(**)行生成一个错误,即无法推导param_type ctor迭代器的类型:
std::discrete_distribution<int>::param_type::param_type(_InIt,_InIt)': template parameter '_InIt' is ambiguous
为什么在两个编译器中推导这种迭代器类型都失败了?输出循环在相同的迭代器中工作正常。
如何解决这个问题?

ybzsozfc

ybzsozfc1#

问题是C中的许多类型是在C20以前的范围范式下创建的,这意味着它们需要一对迭代器。
所有的视图和操作都是基于C20 std::ranges范例来构造范围的。这意味着它们的范围可以由一个迭代器和一个表示结束的sentinel值来定义。sentinel * 不一定是 * 迭代器。对于v0,它确实有一个不是迭代器的sentinel(这是因为iota是一个基于sentinel的范围)。
但是大多数在C
20之前编写的类型在概念上消耗范围,仍然使用范围的成对迭代器模型。因此它们不能与任何使用哨兵的C++20范围一起工作。构造函数模板有一个类型跨越两个参数,但是你的begin/end参数是 * 不同的 * 类型。因此,它不能推导出一个类型。
您需要通过views::common操作将此范围转换为容器或common_range(使用成对迭代器的范围)。

相关问题