5

I am not sure which compiler is right here, as I am not fully proficient with C++20 concepts yet. The following code is rejected by GCC (15.1) saying that there is no matching declaration for Foo::do_foo, Clang (20.1.0) accepts the code. Here it is on Godbolt. Replacing either decltype(x) with U in the return type, or using an unconstrained U for the function parameter makes both compilers happily compile the code. The idea was to for the return type to be a range of const T whenever the passed parameter is const.

Which compiler is "in the right" here? Or, alternatively, is the following correct C++?

#include <concepts>
#include <ranges>
#include <vector>
#include <iostream>

template<typename RangeType, typename ReturnType>
concept RangeReturnTypeIsConvertibleTo = requires(RangeType r) {
    { *r.begin() } -> std::convertible_to<ReturnType>;
};

template<typename R, typename V>
concept RangeOf = std::ranges::range<R> &&
                  RangeReturnTypeIsConvertibleTo<R, V>;


template<typename T, typename V>
concept IsQualificationOf = std::same_as<std::remove_cvref_t<T>, V>;


class Foo {
public:
    using T = int;

    template<IsQualificationOf<T> U>
    auto do_foo(U&& x) -> RangeOf<std::remove_reference_t<decltype(x)>> auto;
};

template<IsQualificationOf<typename Foo::T> U>
auto Foo::do_foo(U&& x) -> RangeOf<std::remove_reference_t<decltype(x)>> auto 
{
    static std::vector<T> bar{4, 5, 8};

    return bar;
}

int main() {

    auto foo = Foo();

    for(auto a : foo.do_foo(4)) std::cout << a << std::endl;

    return 0;
}

GCC error:

<source>:31:6: error: no declaration matches 'auto [requires ::RangeOf<<placeholder>, typename std::remove_reference<decltype(Foo::do_foo::x)>::type>] Foo::do_foo(U&&)'
   31 | auto Foo::do_foo(U&& x) -> RangeOf<std::remove_reference_t<decltype(x)>> auto
      |      ^~~
<source>:26:10: note: candidate is: 'template<class U>  requires  IsQualificationOf<U, int> auto [requires ::RangeOf<<placeholder>, typename std::remove_reference<decltype(Foo::do_foo::x)>::type>] Foo::do_foo(U&&)'
   26 |     auto do_foo(U&& x) -> RangeOf<std::remove_reference_t<decltype(x)>> auto;
      |          ^~~~~~
<source>:20:7: note: 'class Foo' defined here
   20 | class Foo {
      |       ^~~
<source>: In function 'int main()':
<source>:42:28: error: use of 'auto [requires ::RangeOf<<placeholder>, typename std::remove_reference<decltype(Foo::do_foo::x)>::type>] Foo::do_foo(U&&) [with U = int]' before deduction of 'auto'
   42 |     for(auto a : foo.do_foo(4)) std::cout << a << std::endl;
      |                  ~~~~~~~~~~^~~
Compiler returned: 1
1
  • I would bet for gcc/msvc bug (gcc trunk ICE, so definitively a gcc bug there), especially as "equivalent" code (using int instead of Foo::T and U instead of decltype(x)) passes. Commented Jul 31 at 7:49

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.