1

The following code does not compile.

#include <iostream>
#include <memory>
#include <ranges>
#include <vector>


class Base
{
    public:
        virtual ~Base() = default;
        virtual auto say() const -> void = 0;
};

class Derived : public Base
{
    public:
        auto say() const -> void override
        {
            std::cout << "Hi!\n";
        }
};

auto say_all(std::ranges::view auto bases) -> void
{
    for (auto const & base : bases)
    {
        base.say();
    }
}

int main()
{
    std::vector<std::unique_ptr<Derived>> bases;
    bases.push_back(std::make_unique<Derived>());
    bases.push_back(std::make_unique<Derived>());

    say_all(bases | std::views::transform([](auto const & up) { return static_cast<Base &>(*(up.get())); }));
}

The error under gcc is quite lengthy:

<source>: In instantiation of 'main()::<lambda(const auto:11&)> [with auto:11 = std::unique_ptr<Derived>]':
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/type_traits:3302:30:   required from 'struct std::is_invocable<main()::<lambda(const auto:11&)>&, std::unique_ptr<Derived, std::default_delete<Derived> >&>'
 3302 |     : public __bool_constant<__is_invocable(_Fn, _ArgTypes...)>
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/type_traits:3707:71:   required from 'constexpr const bool std::is_invocable_v<main()::<lambda(const auto:11&)>&, std::unique_ptr<Derived, std::default_delete<Derived> >&>'
 3707 |   inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value;
      |                                                                       ^~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/concepts:362:25:   required by substitution of 'template<class _Vp, class _Fp>  requires (input_range<_Vp>) && (copy_constructible<_Fp>) && ((view<_Vp>) && (is_object_v<_Fp>) && (regular_invocable<_Fp&, decltype(*(declval<decltype(std::ranges::__access::__begin((declval<_Container&>)()))&>)())>) && (__can_reference<typename std::invoke_result<_Fp&, decltype(*(declval<decltype(std::ranges::__access::__begin((declval<_Container&>)()))&>)())>::type>)) class std::ranges::transform_view [with _Vp = std::ranges::ref_view<std::vector<std::unique_ptr<Derived> > >; _Fp = main()::<lambda(const auto:11&)>]'
  362 |     concept invocable = is_invocable_v<_Fn, _Args...>;
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:2244:5:   required by substitution of 'template<class _Range, class _Fp> std::ranges::transform_view(_Range&&, _Fp) -> transform_view<views::all_t<_Range>, _Fp> [with _Range = std::vector<std::unique_ptr<Derived> >&; _Fp = main()::<lambda(const auto:11&)>]'
 2244 |     transform_view(_Range&&, _Fp) -> transform_view<views::all_t<_Range>, _Fp>;
      |     ^~~~~~~~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:933:44:   recursively required by substitution of 'template<class _Range>  requires  __adaptor_invocable<_Adaptor, _Range, const _Arg&> constexpr auto std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:11&)> >::operator()(_Range&&) const [with _Range = std::vector<std::unique_ptr<Derived> >&]'
  933 |       = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
      |                    ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:933:44:   required by substitution of 'template<class _Self, class _Range>  requires (__is_range_adaptor_closure<_Self>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&) [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:11&)> >; _Range = std::vector<std::unique_ptr<Derived> >&]'
<source>:37:107:   required from here
   37 |     say_all(bases | std::views::transform([](auto const & up) { return static_cast<Base &>(*(up.get())); }));
      |                                                                                                           ^
<source>:37:72: error: cannot allocate an object of abstract type 'Base'
   37 |     say_all(bases | std::views::transform([](auto const & up) { return static_cast<Base &>(*(up.get())); }));
      |                                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:7:7: note:   because the following virtual functions are pure within 'Base':
    7 | class Base
      |       ^~~~
<source>:11:22: note:     'virtual void Base::say() const'
   11 |         virtual auto say() const -> void = 0;
      |                      ^~~
<source>: In function 'int main()':
<source>:37:19: error: no match for 'operator|' (operand types are 'std::vector<std::unique_ptr<Derived> >' and 'std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:11&)> >')
   37 |     say_all(bases | std::views::transform([](auto const & up) { return static_cast<Base &>(*(up.get())); }));
      |             ~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |             |                            |
      |             |                            std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:11&)> >
      |             std::vector<std::unique_ptr<Derived> >
<source>:37:19: note: there are 7 candidates
   37 |     say_all(bases | std::views::transform([](auto const & up) { return static_cast<Base &>(*(up.get())); }));
      |             ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from <source>:3:
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:981:5: note: candidate 1: 'template<class _Lhs, class _Rhs>  requires (__is_range_adaptor_closure<_Lhs>) && (__is_range_adaptor_closure<_Rhs>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs&&, _Rhs&&)'
  981 |     operator|(_Lhs&& __lhs, _Rhs&& __rhs)
      |     ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:981:5: note: template argument deduction/substitution failed:
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:981:5: note: constraints not satisfied
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges: In substitution of 'template<class _Lhs, class _Rhs>  requires (__is_range_adaptor_closure<_Lhs>) && (__is_range_adaptor_closure<_Rhs>) constexpr auto std::ranges::views::__adaptor::operator|(_Lhs&&, _Rhs&&) [with _Lhs = std::vector<std::unique_ptr<Derived> >&; _Rhs = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, main()::<lambda(const auto:11&)> >]':
<source>:37:107:   required from here
   37 |     say_all(bases | std::views::transform([](auto const & up) { return static_cast<Base &>(*(up.get())); }));
      |                                                                                                           ^
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:962:13:   required for the satisfaction of '__is_range_adaptor_closure<_Lhs>' [with _Lhs = std::vector<std::unique_ptr<Derived, std::default_delete<Derived> >, std::allocator<std::unique_ptr<Derived, std::default_delete<Derived> > > >&]
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:963:9:   in requirements with '_Tp __t' [with _Tp = std::vector<std::unique_ptr<Derived, std::default_delete<Derived> >, std::allocator<std::unique_ptr<Derived, std::default_delete<Derived> > > >&]
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:963:70: note: the required expression 'std::ranges::views::__adaptor::__is_range_adaptor_closure_fn(__t, __t)' is invalid
  963 |       = requires (_Tp __t) { __adaptor::__is_range_adaptor_closure_fn(__t, __t); };
      |                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:972:5: note: candidate 2: 'template<class _Self, class _Range>  requires (__is_range_adaptor_closure<_Self>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&)'
  972 |     operator|(_Range&& __r, _Self&& __self)
      |     ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ranges:972:5: note: substitution of deduced template arguments resulted in errors seen above
In file included from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/memory_resource.h:40,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/string:72,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/locale_classes.h:42,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:43,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ios:46,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ostream.h:43,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/ostream:42,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/iostream:43,
                 from <source>:1:
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/cstddef:141:3: note: candidate 3: 'constexpr std::byte std::operator|(byte, byte)'
  141 |   operator|(byte __l, byte __r) noexcept
      |   ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/cstddef:141:18: note: no known conversion for argument 1 from 'std::vector<std::unique_ptr<Derived> >' to 'std::byte'
  141 |   operator|(byte __l, byte __r) noexcept
      |             ~~~~~^~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:91:3: note: candidate 4: 'constexpr std::_Ios_Fmtflags std::operator|(_Ios_Fmtflags, _Ios_Fmtflags)'
   91 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b) _GLIBCXX_NOTHROW
      |   ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:91:27: note: no known conversion for argument 1 from 'std::vector<std::unique_ptr<Derived> >' to 'std::_Ios_Fmtflags'
   91 |   operator|(_Ios_Fmtflags __a, _Ios_Fmtflags __b) _GLIBCXX_NOTHROW
      |             ~~~~~~~~~~~~~~^~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:150:3: note: candidate 5: 'constexpr std::_Ios_Openmode std::operator|(_Ios_Openmode, _Ios_Openmode)'
  150 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b) _GLIBCXX_NOTHROW
      |   ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:150:27: note: no known conversion for argument 1 from 'std::vector<std::unique_ptr<Derived> >' to 'std::_Ios_Openmode'
  150 |   operator|(_Ios_Openmode __a, _Ios_Openmode __b) _GLIBCXX_NOTHROW
      |             ~~~~~~~~~~~~~~^~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:197:3: note: candidate 6: 'constexpr std::_Ios_Iostate std::operator|(_Ios_Iostate, _Ios_Iostate)'
  197 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b) _GLIBCXX_NOTHROW
      |   ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/ios_base.h:197:26: note: no known conversion for argument 1 from 'std::vector<std::unique_ptr<Derived> >' to 'std::_Ios_Iostate'
  197 |   operator|(_Ios_Iostate __a, _Ios_Iostate __b) _GLIBCXX_NOTHROW
      |             ~~~~~~~~~~~~~^~~
In file included from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/shared_ptr_atomic.h:33,
                 from /cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/memory:83,
                 from <source>:2:
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/atomic_base.h:104:3: note: candidate 7: 'constexpr std::memory_order std::operator|(memory_order, __memory_order_modifier)'
  104 |   operator|(memory_order __m, __memory_order_modifier __mod) noexcept
      |   ^~~~~~~~
/cefs/22/22e6cdc013c8541ce3d1548e_consolidated/compilers_c++_x86_gcc_15.2.0/include/c++/15.2.0/bits/atomic_base.h:104:26: note: no known conversion for argument 1 from 'std::vector<std::unique_ptr<Derived> >' to 'std::memory_order'
  104 |   operator|(memory_order __m, __memory_order_modifier __mod) noexcept
      |             ~~~~~~~~~~~~~^~~
Compiler returned: 1

Basically, it complains it cannot create an object of type Base as it is an abstract class. But why does it try to?

All I am trying to do is to create a view of references to base class and pass it to a function. The code here may not make much sense, but it is distilled from a larger example that uses multiple inheritance and just serves the purpose of illustrating the problem.

1
  • At a guess, because the type deduction on the lambda is dropping the reference and returning a copy. Does [](auto const & up) -> decltype(auto){ return static_cast<Base &>(*(up.get())); }) help? I will be honest, I love ranges, but the template machinery that drives them is pretty opaque to me. Commented Sep 15 at 21:43

1 Answer 1

14

The issue is that the lambda in your transform call is automatically deducing the return type. By default a lambda that doesn't call out a return type uses auto rules for deduction which drops references. See What is the return type of a lambda expression if an item of a vector is returned?

In your case, you can salvage things by explicitly calling out the return type as Base &

#include <iostream>
#include <memory>
#include <ranges>
#include <vector>


class Base
{
    public:
        virtual ~Base() = default;
        virtual auto say() const -> void = 0;
};

class Derived : public Base
{
    public:
        auto say() const -> void override
        {
            std::cout << "Hi!\n";
        }
};

auto say_all(std::ranges::view auto bases) -> void
{
    for (auto const & base : bases)
    {
        base.say();
    }
}

int main()
{
    std::vector<std::unique_ptr<Derived>> bases;
    bases.push_back(std::make_unique<Derived>());
    bases.push_back(std::make_unique<Derived>());

    say_all(bases | std::views::transform([](auto const & up) -> Base & { return *up; }));
}

https://godbolt.org/z/GWfYoa3vx

Sign up to request clarification or add additional context in comments.

2 Comments

Since the return type is now specified, there's no longer any use for static_cast<Base &>. That can be dropped, which conveniently makes the working code shorter than the failed attempt. While you're at it, the question's *(up.get()) is gratuitously complex; it could be simply *up.
Ahh, yes and yes. I did ditch the static_cast and the silly *(up.get()) was a leftover from code that used pointers to base class (and of course compiled fine).

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.