首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++17 :打字机操作

C++17 :打字机操作
EN

Code Review用户
提问于 2021-10-24 06:08:57
回答 3查看 2.8K关注 0票数 8

我正在阅读“现代C++设计”(A. Alexandrescu,2002),并在第三章中练习基本的TMP。由于这本书很过时,我用C++17-esque的方式修改了示例代码。

请随时评论任何事情!

代码语言:javascript
复制
#include <cstddef>
#include <iostream>
#include <utility>
#include <type_traits>

// defining a typelist

template <typename T>
void printType() {
    std::cout << __PRETTY_FUNCTION__ << '\n';
}

template <typename...> struct TypeList;

template <>
struct TypeList<> {};

template <typename Head, typename... Tails>
struct TypeList<Head, Tails...> {
    using head = Head;
    using tails = TypeList<Tails...>;
};

// length of a typelist

template <typename TList> struct Length;

template <typename... Types>
struct Length<TypeList<Types...>> {
    static constexpr std::size_t value = sizeof...(Types);
};

template <typename TList>
inline constexpr std::size_t Length_v = Length<TList>::value;

// indexed access

template <typename TList, std::size_t index> struct TypeAt;

template <typename Head, typename... Tails>
struct TypeAt<TypeList<Head, Tails...>, 0> {
    using type = Head;
};

template <typename Head, typename... Tails, std::size_t index>
struct TypeAt<TypeList<Head, Tails...>, index> {
    static_assert(index < sizeof...(Tails) + 1, "index out of range");
    using type = typename TypeAt<TypeList<Tails...>, index - 1>::type;
};

template <typename TList, std::size_t index>
using TypeAt_t = typename TypeAt<TList, index>::type;

// indexof

template <typename TList, typename T> struct IndexOf;

template <typename T>
struct IndexOf<TypeList<>, T> {
    static constexpr std::size_t value = -1;
};

template <typename TList, typename T>
inline constexpr std::size_t IndexOf_v = IndexOf<TList, T>::value;

template <typename... Tails, typename T>
struct IndexOf<TypeList<T, Tails...>, T> {
    static constexpr std::size_t value = 0;
};

template <typename Head, typename... Tails, typename T>
struct IndexOf<TypeList<Head, Tails...>, T> {
    static constexpr std::size_t value = std::is_same_v<Head, T> ? 0 :
                                         (IndexOf_v<TypeList<Tails...>, T> == -1 ? -1 :
                                          IndexOf_v<TypeList<Tails...>, T> + 1);

};

// appending to typelists

template <typename TList1, typename TList2> struct Append;

template <typename... TList, typename T>
struct Append<TypeList<TList...>, T> {
    using result_type = TypeList<TList..., T>;
};

template <typename T, typename... TList>
struct Append<T, TypeList<TList...>> {
    using result_type = TypeList<T, TList...>;
};

template <typename... TList1, typename... TList2>
struct Append<TypeList<TList1...>, TypeList<TList2...>> {
    using result_type = TypeList<TList1..., TList2...>;
};

template <typename TList1, typename TList2>
using Append_t = typename Append<TList1, TList2>::result_type;

// erasing a type from a typelist

template <typename TList1, typename T> struct Erase;

template <typename TList1, typename T>
using Erase_t = typename Erase<TList1, T>::result_type;

template <typename... Tails, typename T>
struct Erase<TypeList<T, Tails...>, T> {
    using result_type = TypeList<Tails...>;
};

template <typename Head, typename... Tails, typename T>
struct Erase<TypeList<Head, Tails...>, T> {
    using result_type = Append_t<Head, Erase_t<TypeList<Tails...>, T>>;
};

template <typename TList1, typename T> struct EraseAll;

template <typename TList1, typename T>
using EraseAll_t = typename EraseAll<TList1, T>::result_type;

template <typename T>
struct EraseAll<TypeList<>, T> {
    using result_type = TypeList<>;
};

template <typename... Tails, typename T>
struct EraseAll<TypeList<T, Tails...>, T> {
    using result_type = EraseAll_t<TypeList<Tails...>, T>;
};

template <typename Head, typename... Tails, typename T>
struct EraseAll<TypeList<Head, Tails...>, T> {
    using result_type = Append_t<Head, EraseAll_t<TypeList<Tails...>, T>>;
};

// erasing duplicates

template <typename TList> struct NoDuplicates;

template <typename TList>
using NoDuplicates_t = typename NoDuplicates<TList>::result_type;

template <>
struct NoDuplicates<TypeList<>> {
    using result_type = TypeList<>;
};

template <typename Head, typename... Tails>
struct NoDuplicates<TypeList<Head, Tails...>> {
    using result_type = Append_t<Head, EraseAll_t<NoDuplicates_t<TypeList<Tails...>>, Head>>;
};

// replacing an element

template <typename TList, typename Old, typename New> struct Replace;

template <typename TList, typename Old, typename New>
using Replace_t = typename Replace<TList, Old, New>::result_type;

template <typename Old, typename New>
struct Replace<TypeList<>, Old, New> {
    using result_type = TypeList<>;
};

template <typename... Tails, typename Old, typename New>
struct Replace<TypeList<Old, Tails...>, Old, New> {
    using result_type = TypeList<New, Tails...>;
};

template <typename Head, typename... Tails, typename Old, typename New>
struct Replace<TypeList<Head, Tails...>, Old, New> {
    using result_type = Append_t<Head, Replace_t<TypeList<Tails...>, Old, New>>;
};

template <typename TList, typename Old, typename New> struct ReplaceAll;

template <typename TList, typename Old, typename New>
using ReplaceAll_t = typename ReplaceAll<TList, Old, New>::result_type;

template <typename Old, typename New>
struct ReplaceAll<TypeList<>, Old, New> {
    using result_type = TypeList<>;
};

template <typename... Tails, typename Old, typename New>
struct ReplaceAll<TypeList<Old, Tails...>, Old, New> {
    using result_type = ReplaceAll_t<TypeList<New, Tails...>, Old, New>;
};

template <typename Head, typename... Tails, typename Old, typename New>
struct ReplaceAll<TypeList<Head, Tails...>, Old, New> {
    using result_type = Append_t<Head, ReplaceAll_t<TypeList<Tails...>, Old, New>>;
};

// partially ordering typelists

template <typename TList, typename Base> struct MostDerived;

template <typename TList, typename Base>
using MostDerived_t = typename MostDerived<TList, Base>::result_type;

template <typename Base>
struct MostDerived<TypeList<>, Base> {
    using result_type = Base;
};

template <typename Head, typename... Tails, typename T>
struct MostDerived<TypeList<Head, Tails...>, T> {
private:
    using cand = MostDerived_t<TypeList<Tails...>, T>;
public:
    using result_type = std::conditional_t<std::is_base_of_v<cand, Head>, Head, cand>;
};

template <typename TList> struct Derived2Front;

template <typename TList>
using Derived2Front_t = typename Derived2Front<TList>::result_type;

template <>
struct Derived2Front<TypeList<>> {
    using result_type = TypeList<>;
};

template <typename Head, typename... Tails>
struct Derived2Front<TypeList<Head, Tails...>> {
private:
    using TheMostDerived = MostDerived_t<TypeList<Tails...>, Head>;
public:
    using result_type = Append_t<TheMostDerived, Replace_t<TypeList<Tails...>, TheMostDerived, Head>>;
};


class A {
};

class B : public A {
};

class C : public B {
};

int main() {
    using numberList = TypeList<int, double, float, uint8_t>;
    printType<numberList>();
    std::cout << Length_v<numberList> << '\n';
    printType<TypeAt_t<numberList, 2>>();
    std::cout << IndexOf_v<numberList, float> << '\n';

    using charList = TypeList<char, wchar_t, uint8_t>;
    using AppendedList = Append_t<numberList, charList>;
    printType<AppendedList>();

    printType<Erase_t<AppendedList, uint8_t>>();
    printType<EraseAll_t<AppendedList, uint8_t>>();
    printType<NoDuplicates_t<AppendedList>>();
    printType<Replace_t<AppendedList, uint8_t, size_t>>();
    printType<ReplaceAll_t<AppendedList, uint8_t, size_t>>();

    using ChainList = TypeList<A, B, C>;
    printType<MostDerived_t<ChainList, A>>();
    printType<Derived2Front_t<ChainList>>();

}

测试结果

代码语言:javascript
复制
void printType() [with T = TypeList<int, double, float, unsigned char>]
4
void printType() [with T = float]
2
void printType() [with T = TypeList<int, double, float, unsigned char, char, wchar_t, unsigned char>]
void printType() [with T = TypeList<int, double, float, char, wchar_t, unsigned char>]
void printType() [with T = TypeList<int, double, float, char, wchar_t>]
void printType() [with T = TypeList<int, double, float, unsigned char, char, wchar_t>]
void printType() [with T = TypeList<int, double, float, long unsigned int, char, wchar_t, unsigned char>]
void printType() [with T = TypeList<int, double, float, long unsigned int, char, wchar_t, long unsigned int>]
void printType() [with T = C]
void printType() [with T = TypeList<C, B, A>]
EN

回答 3

Code Review用户

回答已采纳

发布于 2021-10-24 09:11:53

总的来说,您的代码看起来非常好。

不需要重载空模板参数包

typename...参数包也匹配零元素,因此没有必要进行以下重载:

代码语言:javascript
复制
template <>
struct TypeList<> {};

使用std::conditional来避免重复代码

您可以使用std::conditional根据某些条件选择类型。这允许您合并一些重载,例如,不再需要声明TypeAt<..., 0>

代码语言:javascript
复制
template <typename Head, typename... Tails, std::size_t index>
struct TypeAt<TypeList<Head, Tails...>, index> {
    static_assert(index < sizeof...(Tails) + 1, "index out of range");
    using type = typename std::conditional<index == 0, Head, TypeAt<TypeList<Tails...>, index - 1>>::type;
};

空打字员缺少的

测试用例

当您编写各种模板时,也要确保测试参数包为空的角落。您的代码很好地处理空打字员!考虑添加以下测试用例:

代码语言:javascript
复制
using EmptyList = TypeList<>;
printType<EmptyList>();
std::cout << Length_v<EmptyList> << '\n';

还测试修改空打字员是否按预期工作,以及从非空打字员中删除所有类型是否会导致空打字员。

票数 11
EN

Code Review用户

发布于 2021-10-24 10:12:03

main()函数中的一个次要点:您忽略了定义uint8_t。我想这应该是来自std::uint8_t<cstdint>,它从另一个库头中泄漏到全局命名空间--但是依赖它是不可移植的!

票数 5
EN

Code Review用户

发布于 2021-10-25 16:54:24

代码语言:javascript
复制
template <typename Head, typename... Tails, std::size_t index>
struct TypeAt<TypeList<Head, Tails...>, index> {
    static_assert(index < sizeof...(Tails) + 1, "index out of range");
    using type = typename TypeAt<TypeList<Tails...>, index - 1>::type;
};

static_assert是一个很晚的失败。您希望在这里早期失败,因此其他代码可以检测到此故障。

代码语言:javascript
复制
template <typename Head, typename... Tails>
struct TypeList<Head, Tails...> {
    using head = Head;
    using tails = TypeList<Tails...>;
};

我不确定正面/反面是否是打字员的基本属性。

我会为打字员写一个函数,返回头尾。

代码语言:javascript
复制
template<class T>
struct tails {};
template<template<class...>class Z, class T0, class...Ts>
struct tails<Z<T0, Ts...>> {
    using type=Z<Ts...>;
};
template<class T>
using tails_t = typename tails<T>::type;
template<class T>
struct head {};
template<template<class...>class Z, class T0, class...Ts>
struct head<Z<T0, Ts...>> {
    using type=head;
};
template<class T>
using head_t = typename head<T>::type;

这现在适用于任何事情(不仅仅是一个TypeList)。

使用时的语法也需要更少的typename垃圾邮件。

..。

接下来要意识到的是,您正在进行函数式编程。有比你所用的更好的模式。

尝试fmap/join/bind:

代码语言:javascript
复制
template<template<class...>class F, class T>
struct fmap {};
template<template<class...>class F, class T>
using fmap_t = typename fmap<F,T>::type;

template<template<class...>class F, template<class...>class Z, class...Ts>
struct fmap<F, Z<Ts...>> {
    using type=Z<F<Ts...>>;
};
template<class L>
struct join;
template<class L>
using join_t = typename join<L>::type;
template<template<class...>class Z, class... T0s, class... T1s, class..Zs>
struct join<Z< Z<T0s...>, Z<T1s...>, Zs... > >:join< Z<Z<T0s...>, Zs... > {};
template<template<class...>class Z, class... T0s, class... T1s, class..Zs>
struct join<Z< Z<T0s...> > > {
    using type=Z<T0s...>;
};
template<template<class...>class F, class T>
using bind_t = join_t< fmap_t< F, T > >;

现在,我们可以使用上面的原语来表示其余的大部分操作。

代码语言:javascript
复制
template<class Ts, class T>
using append_t = join_t< TypeList< Ts, TypeList<T> > >;

擦除只是bind( x->[x] except y->[], Ts )。替换只是bind( x->[x] except y->[z], Ts )

现在,您可能会发现编写这些文件有点烦人。将元编程应用于救援!

在C++17和以后的版本中,您可以使用。您还可以编写lambda的重载。

代码语言:javascript
复制
template<class Match, class Replace>
constexpr auto ReplaceAll = []( auto Types ) {
    return bind(
        overload( [](auto x){return x;}, [](tag_t<Match>){ return tag<Replace>; } ),
        Types
    );
};

将TMP转换为这种类型的元编程和返回可能比编写干净的TMP更容易。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/269320

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档