clang, msvc 可以编译通过, gcc 不行 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Tony042
V2EX    C++

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

  •  
  •   Tony042 2020-08-26 09:01:22 +08:00 3575 次点击
    这是一个创建于 1939 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我又来问问题啦,这回还是喜闻乐见的 C++问题,同样一份代码 MSCVC 19.24 ,clang 10 均可编译通过,g++-10 不可编译通过,这是什么原因呢,注意,须加入参数要求 compiler 支持 C++17 (-std=c++17), 可在线编译版本代码链接 https://godbolt.org/z/Whsne,源码如下, 其中开头部分的 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 条回复    2020-08-27 10:31:52 +08:00
    secondwtq
        1
    secondwtq  
       2020-08-26 09:50:57 +08:00
    瞎改了下把 GCC 改过了 ...
    报错的地方全加个 :: ,变成 ::VariantChoice<Types, Types...> 就行了
    Tony042
        2
    Tony042  
    OP
       2020-08-26 10:04:58 +08:00
    @secondwtq 试了一下确实编译过去啦,估计是 gcc 对模板类继承范围查询出了偏差,谢谢指导~
    secondwtq
        3
    secondwtq  
       2020-08-26 10:24:47 +08:00
    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
        4
    Tony042  
    OP
       2020-08-26 10:33:07 +08:00
    @secondwtq 这个没法删,删了之后就得手写 Variant 的 constructor 了,估计是 gcc 的 bug,看来 gcc bug 还是相对较多的
    0x11901
        5
    0x11901  
       2020-08-26 11:16:25 +08:00
    这就是为什么能用 clang 我都尽量用 clang,gcc 真的不大行
    lewis89
        6
    lewis89  
       2020-08-26 11:52:59 +08:00
    @0x11901 #5 主要还是 C++ 编译器太难实现了...
    zsl199512101234
        7
    zsl199512101234  
       2020-08-26 13:34:08 +08:00
    @lewis89 这句话真赞
    wutiantong
        8
    wutiantong  
       2020-08-26 14:12:02 +08:00
    虽然说不清错在哪,但我们在 Variant 里补一行这个:
    template<typename T> using Choice = VariantChoice<T, Types...>;

    然后把后面的 VariantChoice<XXX, Types...> 都换成 Choice<XXX>,gcc 就没问题了。
    wutiantong
        9
    wutiantong  
       2020-08-26 14:13:50 +08:00
    另外,我个人感觉 clang11 的 Bug 有点多,远不如 clang10
    Tony042
        10
    Tony042  
    OP
       2020-08-26 21:00:47 +08:00
    @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
        11
    wutiantong  
       2020-08-26 23:56:35 +08:00   1
    @Tony042 你好像没有正确理解我的意思,因为我提到的改动并没有改变任何代码含义。与其文字解释不如直接看一下代码:
    https://godbolt.org/z/srW9xo
    Tony042
        12
    Tony042  
    OP
       2020-08-27 01:51:26 +08:00
    @wutiantong 是我理解的有偏差,这样改是没有改变代码含义的,谢谢啦
    Tony042
        13
    Tony042  
    OP
    /div>   2020-08-27 01:55:37 +08:00
    @wutiantong emmm 这样改,clang 就编译不过去了。。。心塞
    Tony042
        14
    Tony042  
    OP
       2020-08-27 01:56:32 +08:00
    @wutiantong 算了,我写宏针对不同编译器,用不同代码好了
    wutiantong
        15
    wutiantong  
       2020-08-27 10:31:52 +08:00
    @Tony042 我找补一下,把 using base-class constructor 那行单独的换回原来的写法:
    using VariantChoice<Types, Types...>::VariantChoice...;

    这样 clang 和 gcc 就都可以了。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4570 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 35ms UTC 05:39 PVG 13:39 LAX 21:39 JFK 00:39
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86