无法在 Clang 上将 std::reference<T> 转换为 std::reference<const T>

Posted

技术标签:

【中文标题】无法在 Clang 上将 std::reference<T> 转换为 std::reference<const T>【英文标题】:Cannot convert std::reference<T> to std::reference<const T> on Clang 【发布时间】:2021-05-05 15:20:35 【问题描述】:

我希望 std::reference_wrapper 可以作为将非常量转换为 const 的参考,例如:

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

以下代码在 MSVC 和 GCC 中编译并运行良好,但在 Clang 中却不行。我只是不明白为什么,它是 UB,还是实际上是 Clang 编译器的问题?

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

仅在 Clang 上,显示以下错误:

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.

【问题讨论】:

Can't reproduce 在 clang 9 及以上。 谢谢@TedLyngmo 你是对的,在编译器资源管理器 x64 上它可以正常工作。但是 ARMV7 版本 clang 显示相同的错误。我不知道为什么它也不能在 wandbox 上工作,因为它是同一个编译器 是的,奇怪的是,当使用与 Godbolt 相同的版本时,它在 wandbox 中不起作用。如果我使用 clang 10.0.1 在我的计算机上本地编译它,它也可以工作。 【参考方案1】:

您在 wandbox 上使用的 std 库有一个错误。它只找到了 2 个构造函数和一个转换运算符。

编译器和标准库并不总是同步的。

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;

通过明确转换它可以工作。我不知道为什么;在我对reference_wrapper 构造和转换运算符的阅读中,没有任何明显的转换差异。

但是缺少隐式引用包装转换可以解释为什么可选需要它。

无论如何,这显然是一个错误。 reference_wrapper&lt;const int&gt; 的通用转换构造函数应适用于:

 void FUN(int const&) 
 FUN(ref)

重载解析有效,它适用于reference_wrapper&lt;int&gt;

【讨论】:

这也是我的 CI 系统中发生的事情。 Linux 版本未编译。会不会是stdlib过时了?

以上是关于无法在 Clang 上将 std::reference<T> 转换为 std::reference<const T>的主要内容,如果未能解决你的问题,请参考以下文章

Clang 无法再使用 <functional> 标头编译程序

无法与忍者建立clang [重复]

为啥 clang 无法展开循环(即 gcc 展开)?

无法将 libc++ 与 clang++-5.0 一起使用

无法弄清楚如何让 CMake 为自定义 clang 驱动程序提取正确的标头

使用带有 NMake 样式 Makefile 的 clang-cl 无法回显