我又来问问题啦,这回还是喜闻乐见的 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;
}
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.