跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • clang, msvc 可以编译通过, gcc 不行
未分類
18 8 月 2020

clang, msvc 可以编译通过, gcc 不行

clang, msvc 可以编译通过, gcc 不行

資深大佬 : Tony042 11

我又来问问题啦,这回还是喜闻乐见的 C++问题,同样一份代码 MSCVC 19.24 ,clang 10 均可编译通过,g++-10 不可编译通过,这是什么原因呢,注意,须加入参数要求 compiler 支持 C++17 (-std=c++17), 可在线编译版本代码链接 https://godbolt.org/z/eWhsne,源码如下, 其中开头部分的 type_traits 应该是没问题的,主要问题集中在 gcc 对 fold expression 的处理上,这份代码是对 std::variant 部分简单不完全实现

#include <type_traits> #include <utility> #include <new> #include <cassert> #include <exception>  template <bool COND, typename TrueType, typename FalseType> class IfThenElseT { public:     using Type = TrueType; };  template <typename TrueType, typename FalseType> class IfThenElseT<false, TrueType, FalseType> { public:     using Type = FalseType; };  template <bool COND, typename TrueType, typename FalseType> using IfThenElse = typename IfThenElseT<COND, TrueType, FalseType>::Type;  template <typename... Elements> class Typelist { };  template <typename List> class FrontT;  template <typename Head, typename... Tail> class FrontT<Typelist<Head, Tail...>> { public:     using Type = Head; };  template <typename List> using Front = typename FrontT<List>::Type;  template <typename List> class PopFrontT;  template <typename Head, typename... Tail> class PopFrontT<Typelist<Head, Tail...>> { public:     using Type = Typelist<Tail...>; };  template <typename List> using PopFront = typename PopFrontT<List>::Type;  template <typename List> class IsEmpty { public:     static constexpr bool value = false; };  template <> class IsEmpty<Typelist<>> { public:     static constexpr bool value = true; };  template <typename List, typename T, unsigned N = 0, bool Empty = IsEmpty<List>::value> struct FindIndexOf { };  template <typename List, typename T, unsigned N> struct FindIndexOf<List, T, N, false> : public IfThenElse<std::is_same_v<Front<List>, T>,                                                           std::integral_constant<unsigned, N>,                                                           FindIndexOf<PopFront<List>, T, N + 1>> { };  template <typename List, typename T, unsigned N> struct FindIndexOf<List, T, N, true> { };  template <typename List> class LargestTypeT;  template <typename List> class LargestTypeT { private:     using First = Front<List>;     using Rest = typename LargestTypeT<PopFront<List>>::Type;  public:     using Type = IfThenElse<(sizeof(First) >= sizeof(Rest)), First, Rest>; };  template <> class LargestTypeT<Typelist<>> { public:     using Type = char; };  template <typename List> using LargestType = typename LargestTypeT<List>::Type;  template <typename... Types> class VariantStorage { private:     using LargestT = LargestType<Typelist<Types...>>;     alignas(Types...) unsigned char buffer[sizeof(LargestT)];     unsigned char discriminator = 0;  public:     unsigned char getDiscriminator() { return discriminator; }     void setDiscriminator(unsigned char d) { discriminator = d; }     void *getRawBuffer() { return buffer; }     void const *getRawBuffer() const { return buffer; }      template <typename T>     T *getBufferAs() { return std::launder(reinterpret_cast<T *>(buffer)); }      template <typename T>     T const *getBufferAs() const { return std::launder(reinterpret_cast<T const *>(buffer)); } };  template <typename... Types> class Variant;  template <typename T, typename... Types> class VariantChoice { private:     using Derived = Variant<Types...>;     Derived &getDerived() { return *static_cast<Derived *>(this); }     Derived const &getDerived() const { return *static_cast<Derived const *>(this); }  protected:     constexpr static unsigned Discriminator = FindIndexOf<Typelist<Types...>, T>::value + 1;  public:     VariantChoice() = default;     VariantChoice(T const &value);     VariantChoice(T &&value);     bool destroy();     Derived &operator=(T const &value);     Derived &operator=(T &&value); };  template <typename T, typename... Types> VariantChoice<T, Types...>::VariantChoice(T const &value) {     new (getDerived().getRawBuffer()) T(value);     getDerived().setDiscriminator(Discriminator); }  template <typename T, typename... Types> VariantChoice<T, Types...>::VariantChoice(T &&value) {     new (getDerived().getRawBuffer()) T(std::move(value));     getDerived().setDiscriminator(Discriminator); }  template <typename T, typename... Types> bool VariantChoice<T, Types...>::destroy() {     if (getDerived().getDiscriminator() == Discriminator)     {         getDerived().template getBufferAs<T>()->~T();         return true;     }     return false; }  template <typename T, typename... Types> auto VariantChoice<T, Types...>::operator=(T const &value) -> Derived & {     if (getDerived().getDiscriminator() == Discriminator)     {         *getDerived().template getBufferAs<T>() = value;     }     else     {         getDerived().destroy();         new (getDerived().getRawBuffer()) T(value);         getDerived().setDiscriminator(Discriminator);     }     return getDerived(); }  template <typename T, typename... Types> auto VariantChoice<T, Types...>::operator=(T &&value) -> Derived & {     if (getDerived().getDiscriminator() == Discriminator)     {         *getDerived().template getBufferAs<T>() = std::move(value);     }     else     {         getDerived().destroy();         new (getDerived().getRawBuffer()) T(std::move(value));         getDerived().setDiscriminator(Discriminator);     }     return getDerived(); }  class ComputedResultType;  class EmptyVariant : public std::exception { };  template <typename... Types> class Variant : private VariantStorage<Types...>, private VariantChoice<Types, Types...>... {     template <typename T, typename... OtherTypes>     friend class VariantChoice;  public:     using VariantChoice<Types, Types...>::VariantChoice...;  //g++报错     using VariantChoice<Types, Types...>::operator=...;  //g++报错      template <typename T>     bool is() const;      template <typename T>     T &get() &;      template <typename T>     T &&get() &&;      template <typename T>     T const &get() const &;      // template <typename R=ComputedResultType, typename Visitor>     // VisitResult<R, Visitor      bool empty() const;     void destroy();     ~Variant() { destroy(); }  private: };  template <typename... Types> template <typename T> bool Variant<Types...>::is() const {     return this->getDiscriminator() == VariantChoice<T, Types...>::Discriminator; //g++报错 }  template <typename... Types> template <typename T> T &Variant<Types...>::get() & {     if (empty())     {         throw EmptyVariant();     }     assert(is<T>());     return *this->template getBufferAs<T>(); }  template <typename... Types> template <typename T> T &&Variant<Types...>::get() && {     if (empty())     {         throw EmptyVariant();     }     assert(is<T>());     return *this->template getBufferAs<T>(); }  template <typename... Types> template <typename T> T const &Variant<Types...>::get() const & {     if (empty())     {         throw EmptyVariant();     }     assert(is<T>());     return *this->template getBufferAs<T>(); }  template <typename... Types> void Variant<Types...>::destroy() {     // bool results[] = {VariantChoice<Types, Types...>::destroy()...};     (VariantChoice<Types, Types...>::destroy(), ...);  //g++报错     this->setDiscriminator(0); }  int main() {     Variant<int> v{17};     return 0; } 

大佬有話說 (15)

  • 資深大佬 : secondwtq

    瞎改了下把 GCC 改过了 …
    报错的地方全加个 :: ,变成 ::VariantChoice<Types, Types…> 就行了

  • 主 資深大佬 : Tony042

    @secondwtq 试了一下确实编译过去啦,估计是 gcc 对模板类继承范围查询出了偏差,谢谢指导~

  • 資深大佬 : secondwtq

    reduce 了半天 case,好像删了 using VariantChoice<Types, Types…>::VariantChoice… 这句就没事了

    然后找到了俩 GCC 的 bug report:
    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94310
    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79094

  • 主 資深大佬 : Tony042

    @secondwtq 这个没法删,删了之后就得手写 Variant 的 constructor 了,估计是 gcc 的 bug,看来 gcc bug 还是相对较多的

  • 資深大佬 : 0x11901

    这就是为什么能用 clang 我都尽量用 clang,gcc 真的不大行

  • 資深大佬 : lewis89

    @0x11901 #5 主要还是 C++ 编译器太难实现了…

  • 資深大佬 : zsl199512101234

    @lewis89 这句话真赞

  • 資深大佬 : wutiantong

    虽然说不清错在哪,但我们在 Variant 里补一行这个:
    template<typename T> using Choice = VariantChoice<T, Types…>;

    然后把后面的 VariantChoice<XXX, Types…> 都换成 Choice<XXX>,gcc 就没问题了。

  • 資深大佬 : wutiantong

    另外,我个人感觉 clang11 的 Bug 有点多,远不如 clang10

  • 主 資深大佬 : Tony042

    @wutiantong 额,这个不能这么改,这样改就扩大继承的 VariantChoice 的 Constructor 范围了,VariantChoice<T, Types>中的 T 必须是 parameter pack Types 中的一个类型,Variant 本质上是一个半自动的被赋值为 Types 中任意类型的变量,这就是为什么要用 using VariantChoice<Types, Types…>::operator=…; ,第二个 parameter pack 在尖括号里展开,第一个 parameter pack 在外面展开,继承 number=sizeof…(Types)个 constructor,本质上 Variant 继承 VariantChoice 的 constructor 再通过 VariantStorage 手动管理内存

  • 資深大佬 : wutiantong

    @Tony042 你好像没有正确理解我的意思,因为我提到的改动并没有改变任何代码含义。与其文字解释不如直接看一下代码:
    https://godbolt.org/z/srW9xo

  • 主 資深大佬 : Tony042

    @wutiantong 是我理解的有偏差,这样改是没有改变代码含义的,谢谢啦

  • 主 資深大佬 : Tony042

    @wutiantong emmm 这样改,clang 就编译不过去了。。。心塞

  • 主 資深大佬 : Tony042

    @wutiantong 算了,我写宏针对不同编译器,用不同代码好了

  • 資深大佬 : wutiantong

    @Tony042 我找补一下,把 using base-class constructor 那行单独的换回原来的写法:
    using VariantChoice<Types, Types…>::VariantChoice…;

    这样 clang 和 gcc 就都可以了。

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具