Frank
Frank

Reputation: 545

Can not deduce type of ranges iterator

The following code fails to compile both on MSVC 2022 and gcc 12.1 (with c++20 enabled) with the same error:

#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;
}

The line (**) generates an error that the type of the param_type ctor iterators can not be deduced:

std::discrete_distribution<int>::param_type::param_type(_InIt,_InIt)': template parameter '_InIt' is ambiguous

Why does the deduction of this iterator type fail with both compiler? The output loop works fine with the same iterator.

How can this be fixed?

Upvotes: 1

Views: 184

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 473232

The problem is that many types in C++ were created under the pre-C++20 range paradigm. This means that they take a pair of iterators.

All of your views and operations construct ranges based on the C++20 std::ranges paradigm. This means that their ranges can be defined by an iterator and a sentinel value representing the end. The sentinel does not have to be an iterator. And for the case of v0, it does indeed have a sentinel that is not an iterator (this is because iota is a sentinel-based range).

But most types that were written pre-C++20 which conceptually consume ranges still use the paired iterator model of ranges. So they cannot work with any C++20 range that uses a sentinel. The constructor template has one type across two parameters, but your begin/end arguments are different types. Therefore, it fails to deduce the one type.

You will need to convert this range into either a container or a common_range (a range using paired iterators) via the views::common operation.

Upvotes: 4

Related Questions