Cannot convert std::reference<T> to std::reference<const T> on Clang

Viewed 405

I would expect that std::reference_wrapper would work as a reference in terms of converting non-const into const, like:

int a = 10;
int& refA = a;
const int& constRefA = refA;

The following code compiles and works fine in MSVC and GCC, but not on Clang. I just don't understand why, is it UB, or actually an issue on Clang compiler?

#include <functional>
#include <optional>

int main()
{
    int a = 10;

    std::reference_wrapper<int> ref = a;
    std::reference_wrapper<const int> constRef = ref;

    std::optional<std::reference_wrapper<int>> optRef = a;
    std::optional<std::reference_wrapper<const int>> optConstRef = optRef;

    return 0;
}

On Clang only, displays the following error:

prog.cc:13:39: error: no viable conversion from 'reference_wrapper<int>' to 'reference_wrapper<const int>'
std::reference_wrapper<const int> constRef = ref;

https://wandbox.org/permlink/FSY4tCvE9B17hbVn

prog.cc:13:39: error: no viable conversion from 'reference_wrapper<int>' to 'reference_wrapper<const int>'
    std::reference_wrapper<const int> constRef = ref;
                                      ^          ~~~
/opt/wandbox/clang-head/include/c++/v1/__functional_base:374:28: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'std::reference_wrapper<int>' to 'const std::reference_wrapper<const int> &' for 1st argument
class _LIBCPP_TEMPLATE_VIS reference_wrapper
                           ^
/opt/wandbox/clang-head/include/c++/v1/__functional_base:374:28: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'std::reference_wrapper<int>' to 'std::reference_wrapper<const int> &&' for 1st argument
/opt/wandbox/clang-head/include/c++/v1/__functional_base:386:5: note: candidate constructor not viable: no known conversion from 'std::reference_wrapper<int>' to 'std::reference_wrapper<const int>::type &' (aka 'const int &') for 1st argument
    reference_wrapper(type& __f) _NOEXCEPT
    ^
/opt/wandbox/clang-head/include/c++/v1/__functional_base:389:14: note: candidate constructor not viable: no known conversion from 'std::reference_wrapper<int>' to 'std::reference_wrapper<const int>::type &&' (aka 'const int &&') for 1st argument
    private: reference_wrapper(type&&); public: // = delete; // do not bind to temps
             ^
/opt/wandbox/clang-head/include/c++/v1/__functional_base:394:5: note: candidate function
    operator type&() const _NOEXCEPT {return *__f_;}
    ^
prog.cc:16:54: error: no viable conversion from 'optional<reference_wrapper<int>>' to 'optional<reference_wrapper<const int>>'
    std::optional<std::reference_wrapper<const int>> optConstRef = optRef;
                                                     ^             ~~~~~~
/opt/wandbox/clang-head/include/c++/v1/optional:689:41: note: candidate constructor not viable: no known conversion from 'std::optional<std::reference_wrapper<int>>' to 'const std::optional<std::reference_wrapper<const int>> &' for 1st argument
    _LIBCPP_INLINE_VISIBILITY constexpr optional(const optional&) = default;
                                        ^
/opt/wandbox/clang-head/include/c++/v1/optional:690:41: note: candidate constructor not viable: no known conversion from 'std::optional<std::reference_wrapper<int>>' to 'std::optional<std::reference_wrapper<const int>> &&' for 1st argument
    _LIBCPP_INLINE_VISIBILITY constexpr optional(optional&&) = default;
                                        ^
/opt/wandbox/clang-head/include/c++/v1/optional:691:41: note: candidate constructor not viable: no known conversion from 'std::optional<std::reference_wrapper<int>>' to 'std::nullopt_t' for 1st argument
    _LIBCPP_INLINE_VISIBILITY constexpr optional(nullopt_t) noexcept {}
                                        ^
/opt/wandbox/clang-head/include/c++/v1/optional:715:15: note: candidate template ignored: substitution failure [with _Up = std::optional<std::reference_wrapper<int>> &]: no member named '_EnableIfImpl' in 'std::_MetaBase<false>'
    constexpr optional(_Up&& __v)
              ^
/opt/wandbox/clang-head/include/c++/v1/optional:730:5: note: candidate template ignored: substitution failure [with _Up = std::reference_wrapper<int>]: no member named '_EnableIfImpl' in 'std::_MetaBase<false>'
    optional(const optional<_Up>& __v)
    ^
/opt/wandbox/clang-head/include/c++/v1/optional:748:5: note: candidate template ignored: substitution failure [with _Up = std::reference_wrapper<int>]: no member named '_EnableIfImpl' in 'std::_MetaBase<false>'
    optional(optional<_Up>&& __v)
    ^
/opt/wandbox/clang-head/include/c++/v1/optional:701:24: note: explicit constructor is not a candidate
    constexpr explicit optional(_InPlaceT, _Args&&... __args)
                       ^
/opt/wandbox/clang-head/include/c++/v1/optional:722:24: note: explicit constructor is not a candidate
    constexpr explicit optional(_Up&& __v)
                       ^
/opt/wandbox/clang-head/include/c++/v1/optional:738:14: note: explicit constructor is not a candidate
    explicit optional(const optional<_Up>& __v)
             ^
/opt/wandbox/clang-head/include/c++/v1/optional:756:14: note: explicit constructor is not a candidate
    explicit optional(optional<_Up>&& __v)
             ^
2 errors generated.
1 Answers

The std library you are using on wandbox has a bug. It only found 2 constructors and a conversion operator.

Compilers and standard libraries are not always in sync.

printf("Hello World");
int a = 10;

std::reference_wrapper<int> ref = a;
std::reference_wrapper<const int> constRef( ref );

std::optional<std::reference_wrapper<int>> optRef = a;
std::optional<std::reference_wrapper<const int>> optConstRef( optRef );

return 0;

By making the conversions explicit it works. I don't know why; there aren't any explicit conversions differences in my reading of reference_wrapper construction and conversion operators.

But the lack of implicit reference wrapping conversion would explain why the optional would want it.

In any case, this is clearly a bug. The universal conversion constructor for reference_wrapper<const int> should apply if:

 void FUN(int const&) {}
 FUN(ref)

overload resolution works, and it does for a reference_wrapper<int>.

Related