【问题标题】:get<string> for variants fail under clang++ but not g++变体的 get<string> 在 clang++ 下失败,但在 g++ 下失败
【发布时间】:2018-03-12 09:29:42
【问题描述】:

以下代码:

variant<string> x = "abc";
cout << get<string>(x) << "\n";

在 g++(7.2 版)下工作正常。但是,在 clang++(5.0 版)下使用 libstdc++ 编译时,get 方法中出现以下错误:

/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/7.2.0/../../../../include/c++/7.2.0/variant:238:46: fatal error: cannot cast 'std::variant<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >' to its private base class 'std::__detail::__variant::_Variant_storage<false, std::
__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >'
      return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u);

这是编译器错误,还是我的代码以任何方式非法?

【问题讨论】:

  • 您是否指定libc++ 应该用作标准库实现? IIRC,clang 默认使用 libstdc++。
  • 我看不出这里有什么非法的,但是,如果是这样,这将是一件非常基本的事情......
  • 鉴于它在 gcc+libstdc++ 中工作,在 clang+libc++ 中工作,但在 clang+libstdc++ 中失败,最有可能的情况是它在 clang 和 libstdc++ 之间存在细微的不兼容。答案应该准确指出不兼容的原因。
  • 嘿。今天遇到了同样的问题。

标签: c++ c++17 clang++ variant


【解决方案1】:

这是由clang bug 31852(以及33222)引起的,由 Jonathan Wakely 提供的复制应该看起来非常相关:

template<typename V> auto get(V&) { }

template<typename>
class variant
{
    template<typename V> friend auto get(V&);
};

int main()
{
  variant<int> v{};
  get(v); // error: ambiguous 
}

clang 不能正确识别具有占位符类型的友元声明。这正是 libstdc++ 实现std::get 的方式:

// Returns the typed storage for __v.
template<size_t _Np, typename _Variant>
constexpr decltype(auto) __get(_Variant&& __v)
{
    return __get(std::in_place_index<_Np>, std::forward<_Variant>(__v)._M_u);
}

这访问了variant的一个私有成员,但是这个函数是properly declared a friend

template<size_t _Np, typename _Vp>
friend constexpr decltype(auto) __detail::__variant::__get(_Vp&& __v);

libstdc++ 的实现是有效的,clang 只是不认为__getfriend

【讨论】:

  • 我认为最好且相当安全的解决方法是在开发过程中使用 libstdc++ 的变体头文件的修改版本。您需要更改的只是注释掉朋友声明并公开派生自 __detail::_variant::_Variant_base (这使 __get 可以访问底层变量存储)。在使用 GCC 作为主编译器时,我使用它来消除大量来自 clang-tidy 的错误,并且没有注意到任何不良副作用。
  • 更改基类的访问权限是大材小用,只需在variant 类定义的末尾添加public: using _Base::_M_u; 即可。我已经在gcc.gnu.org/r258854
  • 此错误现已修复! :)
  • 在 C++17 之前和 Clang 7 之前构建时,一个可能的解决方法也是回退到 boost::variant。它只是标题,差异很小。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 2011-07-14
  • 2023-03-21
  • 1970-01-01
  • 2019-07-31
  • 1970-01-01
相关资源
最近更新 更多