草庐IT

C++14 类型列表,有什么理由更喜欢 'free functions' 到 'methods',反之亦然?

coder 2023-06-03 原文

我看到了在 C++11/14 中实现类型列表的两种可能的风格,我很好奇是否有任何理由更喜欢其中一种。第一种技术是outlined here并在 Boost 的 MPL 库上建模。在这种风格中,您定义了接收类型列表并对其进行操作的元“自由函数”(使用声明的顶层)。以下是您将如何实现 std::transform 的元版本,该版本适用于类型而不是第一种样式中的值:

    template <typename... Args>
    struct type_list;

    namespace impl
    {
        template <template <typename...> class F, class L>
        struct transform_impl;

        template <template <typename...> class F, template <typename...> class L, typename... T>
        struct transform_impl<F, L<T...>>
        {
            using type = L<typename F<T>::type...>;
        };
    }

    template <template <typename...> class F, class L>
    using transform = typename impl::transform_impl<F, L>::type;

第二种风格是定义元“方法”(使用类型列表结构内的声明)。下面是变换在该样式中的外观:

    template <typename... Args>
    struct type_list {
        // ... other 'methods'

        template<template<class> class Wrapper>
        using transform =
            type_list<Wrapper<Args>...>;

        // ... other 'methods'
    };

我在第二种样式中看到的优点是您仍然可以使用 Args... 参数包,因此您不必委托(delegate)给 impl 助手功能。两个可能的缺点是 1)您必须将所有元函数放在 type_list 中,而不是可能将它们放在单独的标题中,因此您会失去一些模块化和 2)“免费”元函数也适用于元组和任何其他可变参数模板开箱即用的类(class)。我不知道在实践中对 #2 的渴望到底有多普遍,我自己也只是发现了使用 type_list 和 tuple 的场合,编写元代码在 type_list 和 tuple 之间进行转换并不难。

是否有充分的理由强烈偏爱其中一种?也许#2实际上是一个常见的情况?

最佳答案

第二个不好有很多原因。

首先,称它为一团糟。模板内的模板需要使用 template关键字。

其次,它要求您的类型列表在其正文中包含您想要对类型列表执行的每个操作。这就像在 string 上定义每个操作。作为字符串上的方法:如果您允许自由函数,则可以创建新操作,甚至可以实现覆盖。

最后,考虑隐藏 ::type :

从这些原语开始:

template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<class...Ts>struct types : tag<types<Ts...>>{};

转换,或 fmap ,然后看起来像:

template<template<class...>class Z, class Types>
struct fmap;
template<template<class...>class Z, class...Ts>
struct fmap<Z, types<Ts...>>:types<Z<Ts...>>{};
template<template<class...>class Z, class Types>
using fmap_t = type_t<fmap<Z,Types>>;

您可以使用type_t<fmap<Z,types<int,double>>> , 或 fmap_t<Z,types<int,double>>获取映射到类型的类型。

另一种方法是使用 constexpr包含各种东西的函数:

template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class Tag>using type_t=typename Tag::type;

template<template<class...>class Z>
struct z {template<class...Ts>using apply=Z<Ts...>; constexpr z(){};};
template<class...Ts>
struct one_type {};
template<class T0>
struct one_type<T0> { using type=T0; };
template<class...Ts>
using one_type_t=typename one_type<Ts...>::type;

template<template<class>class Z>
struct z_one_base {
    template<class...Ts>
    using helper = Z<one_type_t<Ts...>>;
    using type = z<helper>;
};
template<template<class>class Z>
using z_one = type_t<z_one_base<Z>>;

现在 fmap很简单:

// take a template metafunction and a list of types
// and apply the metafunction to each type, returning the list
template<template<class...>class Z, class...Ts>
constexpr auto fmap( z<Z>, types<Ts...> )
-> types<Z<Ts>...> { return {}; }

其他功能如下:

// a template metafunction and a list of types
// and apply the template metafunction to all of the types
template<template<class...>class Z, class...Ts>
constexpr auto apply( z<Z>, types<Ts...> )
-> tag<Z<Ts...>> { return {}; }

// take any number of tags
// and make a type list from them
template<class...Tags>
constexpr auto make_list( Tags... )
-> types<type_t<Tags>...> { return {}; }

// concat of nothing is an empty list
constexpr types<> concat() { return {}; }
// concat of a list alone is a list alone:
template<class...T1s>
constexpr auto concat(types<T1s...>)
->types<T1s...>{ return {}; }
// concat of 2 or more lists is the concat of the first two,
// concatted with the rest
template<class...T1s, class...T2s, class...Types>
constexpr auto concat(types<T1s...>,types<T2s...>,Types...)
->decltype( concat(types<T1s...,T2s...>{},Types{}...) )
{ return {}; }


// take a tagged list or a tagged type, and return a list
template<class T>
constexpr auto fbox( tag<T> )->types<T> { return {}; }
template<class...Ts>
constexpr auto fbox( tag<types<Ts...>> )->types<Ts...> { return {}; }

// create z_ versions of functions above:
#define CAT2(A,B) A##B
#define CAT(A,B) CAT2(A,B)
// lift functions to metafunctions with z_ prefix:
#define Z_F(F) \
  template<class...Ts> \
  using CAT(meta_, F) = decltype( F( Ts{}... ) ); \
  using CAT(CAT(z_, F),_t) = z<CAT(meta_, F)>; \
  static constexpr CAT(CAT(z_, F),_t) CAT(z_, F){}

Z_F(concat);
//Z_F(apply);
//Z_F(fmap);
Z_F(fbox);
static constexpr z_one<tag> z_tag{};


// joins a list of lists or types into a list of types
template<class...Ts>
constexpr auto join1(types<Ts...>)
->type_t<decltype( apply( z_concat, fmap( z_fbox, types<tag<Ts>...>{} ) ) )>
{ return {}; }
template<class Types>
constexpr auto join(Types types)
->type_t<decltype( apply( z_concat, fmap( z_fbox, fmap( z_tag, types ) ) ) )>
{ return {}; }

template<class Z, class...Ts>
constexpr auto fbind(Z z, Ts...ts)
->decltype( join( fmap( z, ts... ) ) )
{ return {}; }

并使用伪类型(tag s)而不是直接在顶层使用类型。如果您需要使用 type_t 恢复类型当你想要的时候。

我认为这是 boost::hana喜欢的方法,但我才开始看boost::hana .这里的优点是我们将类型包从操作中解耦,我们可以访问完整的 C++ 重载(而不是模板模式匹配,这可能更脆弱),并且我们可以直接推断类型包的内容而不必做using和empty-primary-specialization技巧。

所有消耗的都是 tag<?> 的包装类型或 types<?>z<?> ,所以没有什么是“真实的”。

测试代码:

template<class T> using to_double = double;
template<class T> using to_doubles = types<double>;

int main() {
    types< int, int, int > three_ints;

    auto three_double = fmap( z_one<to_double>{}, three_ints );
    three_double  = types<double, double, double >{};
    auto three_double2 = join( fmap( z_one<to_doubles>{}, three_ints ) );
    three_double = three_double2;
    auto three_double3 = fbind( z_one<to_doubles>{}, three_ints );
    three_double3 = three_double2;
}

Live example .

关于C++14 类型列表,有什么理由更喜欢 'free functions' 到 'methods',反之亦然?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30809531/

有关C++14 类型列表,有什么理由更喜欢 'free functions' 到 'methods',反之亦然?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  3. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  4. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  5. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  6. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  7. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  8. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  9. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  10. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

随机推荐