在clang++下使用get<string>获取变量失败,但在g++下却没有问题

26
代码如下:

以下代码:

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

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

在 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++ 作为标准库实现?如果我没记错,clang 默认使用的是 libstdc++。 - StoryTeller - Unslander Monica
我看不出这里有什么违法的地方,但如果真有的话,那就是一个相当基础的错误了... - Sam Varshavchik
2
鉴于它在gcc+libstdc++中运行正常,在clang+libc++中也能正常工作,但在clang+libstdc++中失败,最有可能的情况是clang和libstdc++之间存在微妙的不兼容性。然而,答案应该明确指出这种不兼容性是什么。 - user743382
今天我也遇到了这个问题。 - Jagoly
1个回答

24

这是由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 的私有成员,但是这个函数已经正确声明为friend:

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

libstdc++的实现是有效的,clang只是认为__get不是一个friend


3
我认为最好且相对安全的解决方法是在开发过程中使用修改后的libstdc++的variant头文件。你只需要将friend声明注释掉,并公开继承自__detail::_variant::_Variant_base(这会提供__get访问底层变量存储)。我使用这种方法,在将GCC作为主编译器时,消除了来自clang-tidy的大量错误,而且没有注意到任何不良副作用。 - Stacker
7
修改基类的访问权限有些过度,只需要在variant类定义的末尾添加public: using _Base::_M_u;即可。我在https://gcc.gnu.org/r258854中已经这样做了。 - Jonathan Wakely
1
这个 bug 现在已经被修复了! :) - Rakete1111
一个可能的解决方法是在使用早期版本的C++17和早期版本的Clang时,回退到boost::variant。它只有头文件,差异很小。 - Giovanni Cerretani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接