ericniebler / range-v3

Range library for C++14/17/20, basis for C++20's std::ranges
Other
4.15k stars 439 forks source link

"ranges::to" failed for template type T with universal construction #1794

Closed YanB25 closed 1 year ago

YanB25 commented 1 year ago

Minimal Reproducible Codes

template <typename T>
struct Wrapper
{
    template <typename... Args>
    Wrapper(Args &&...args) : t_(std::forward<Args>(args)...)
    {
    }
    T t_;
};

int main()
{
    using namespace ranges;

    std::vector<Wrapper<int>> vec;
    // Compile Error at `| to<std::vector>`
    auto v = views::all(vec) | to<std::vector>();
}

Expected Behaviour

The code should compile.

Compile Error Messages

/home/yanbin/sherman/test/trivial.cpp:247:31: error: no viable conversion from 'Wrapper<int>' to 'int'
    Wrapper(Args &&...args) : t_(std::forward<Args>(args)...)
                              ^  ~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_construct.h:109:38: note: in instantiation of function template specialization 'Wrapper<int>::Wrapper<Wrapper<int> &>' requested here
    { ::new(static_cast<void*>(__p)) _Tp(std::forward<_Args>(__args)...); }
                                     ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:91:8: note: in instantiation of function template specialization 'std::_Construct<Wrapper<int>, Wrapper<int> &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:150:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<__gnu_cxx::__normal_iterator<Wrapper<int> *, std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > >, Wrapper<int> *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<__gnu_cxx::__normal_iterator<Wrapper<int> *, std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > >, Wrapper<int> *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/vector.tcc:330:13: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator<Wrapper<int> *, std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > >, Wrapper<int> *, Wrapper<int> >' requested here
              std::__uninitialized_copy_a(__mid, __last,
                   ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:1628:4: note: in instantiation of function template specialization 'std::vector<Wrapper<int>, std::allocator<Wrapper<int> > >::_M_assign_aux<__gnu_cxx::__normal_iterator<Wrapper<int> *, std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > > >' requested here
        { _M_assign_aux(__first, __last, std::__iterator_category(__first)); }
          ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/bits/stl_vector.h:769:4: note: in instantiation of function template specialization 'std::vector<Wrapper<int>, std::allocator<Wrapper<int> > >::_M_assign_dispatch<__gnu_cxx::__normal_iterator<Wrapper<int> *, std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > > >' requested here
        { _M_assign_dispatch(__first, __last, __false_type()); }
          ^
/home/yanbin/sherman/thirdparty/range-v3/include/range/v3/range/conversion.hpp:330:19: note: in instantiation of function template specialization 'std::vector<Wrapper<int>, std::allocator<Wrapper<int> > >::assign<__gnu_cxx::__normal_iterator<Wrapper<int> *, std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > >, void>' requested here
                c.assign(I{ranges::begin(rng)}, I{ranges::end(rng)});
                  ^
/home/yanbin/sherman/thirdparty/range-v3/include/range/v3/range/conversion.hpp:276:24: note: in instantiation of function template specialization 'ranges::detail::to_container::fn<ranges::detail::from_range<std::vector> >::operator()<ranges::ref_view<std::vector<Wrapper<int>, std::allocator<Wrapper<int> > > > >' requested here
                return static_cast<Fn &&>(fn)(static_cast<Rng &&>(rng));
                       ^
1 error generated.
CMakeFiles/trivial.dir/build.make:75: recipe for target 'CMakeFiles/trivial.dir/test/trivial.cpp.o' failed
make[3]: *** [CMakeFiles/trivial.dir/test/trivial.cpp.o] Error 1
CMakeFiles/Makefile2:2217: recipe for target 'CMakeFiles/trivial.dir/all' failed
make[2]: *** [CMakeFiles/trivial.dir/all] Error 2
CMakeFiles/Makefile2:2224: recipe for target 'CMakeFiles/trivial.dir/rule' failed
make[1]: *** [CMakeFiles/trivial.dir/rule] Error 2
Makefile:1112: recipe for target 'trivial' failed
make: *** [trivial] Error 2

Env

clang version 10.0.0-4ubuntu1~18.04.2 
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
NAME="Ubuntu"
VERSION="18.04.3 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.3 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

Also, I don't know whether it is a bug or an expected behaviour (a feature). Please let me know if you can help me workaround this case.

brevzin commented 1 year ago

The problem is in your code, not range-v3:

int main() {
    Wrapper<int> a;
    Wrapper<int> b = a; // error
}

The ranges::to construction fails ultimately for the same reason that the construction of b there fails. You need to be careful when writing forwarding reference constructors that you don't take priority over the copy constructor from a non-const lvalue.

YanB25 commented 1 year ago

Thank you to your timely reply! It works.

And I realized a good way to implement this wrapper is similar to std::optional's constructor: taking an std::in_place_t.