草庐IT

c++ - 如何通过未定义类型定义元函数?

coder 2024-02-20 原文

请考虑像这样的元函数

#include <type_traits>

template <typename T, T N, T M>
struct Sum : std::integral_constant <T, N + M> {};

template <typename T, T N, T M>
struct Product : std::integral_constant <T, N * M> {};

他们的结果可以通过::value提取出来成员:

static_assert (Sum <int, 3, 4>::value == 7, "3 + 4 == 7");
static_assert (Product <int, 2, 5>::value == 10, "2 * 5 == 10");

两个元函数都有相似的静态签名。也就是说,他们关联了一个 T每对 T在哪里T受到与 std::integral_constant 相同的限制。并且要么是可加的,要么是可乘的。所以我们可以创建一个通用元函数来进行评估。

template <typename T, template <typename U, U, U> class F, T N, T M>
struct EvaluateBinaryOperator : std::integral_constant <T, F <T, N, M>::value> {};

static_assert (EvaluateBinaryOperator <int, Sum, 3, 4>::value == 7, "3 + 4 == 7");
static_assert (EvaluateBinaryOperator <int, Product, 2, 5>::value == 10, "2 * 5 == 10");

当单独以这种形式使用时,污染 Sum 感觉是多余的和 Product具有 std::integral_constant 的结构.为了向您展示我们确实可以做到,请考虑以下几点:

template <typename T, T N, T M, T R = N + M>
struct Sum;

template <typename T, T N, T M, T R = N * M>
struct Product;

template <typename> struct EvaluateBinaryOperator;

template <typename T, template <typename U, U, U, U> class F, T N, T M, T R>
struct EvaluateBinaryOperator <F <T, N, M, R> > : std::integral_constant <T, R> {};

static_assert (EvaluateBinaryOperator <Sum <int, 3, 4> >::value == 7, "3 + 4 == 7");
static_assert (EvaluateBinaryOperator <Product <int, 2, 5> >::value == 10, "2 * 5 == 10");

而不是使用 Sum 的成员和 Product ,我们专注于默认参数并仅在 EvaluateBinaryOperator 中提取它.作为额外的奖励,SumProduct可以保持没有定义,使它们变得平凡不可推断和不可构造,并且语法看起来也更清晰。现在,问题来了。如果我们希望我们所有的元函数都有一个统一的静态接口(interface)怎么办?也就是说,如果我们引入

template <typename...> struct Tuple;

template <typename T, T> struct Value;

并要求我们所有的元函数看起来像template <typename> struct ?例如,

template <typename> struct Sum;

template <typename T, T N, T M>
struct Sum <Tuple <Value <T, N>, Value <T, M> > > : 
    std::integral_constant <T, N + M> {};

template <typename> struct Product;

template <typename T, T N, T M>
struct Product <Tuple <Value <T, N>, Value <T, M> > > : 
    std::integral_constant <T, N * M> {};

现在,我们想将它们转换成类似这样的东西:

template <typename, typename> struct Sum;

template <typename T, T N, T M, typename R = Tuple <Value <T, N + M> > >
struct Sum <Tuple <Value <T, N>, Value <T, M> >, R>;

template <typename, typename> struct Product;

template <typename T, T N, T M, typename R = Tuple <Value <T, N * M> > >
struct Product <Tuple <Value <T, N>, Value <T, M> >, R>;

这样我们就可以提取值

template <typename> struct Evaluate;

template <template <typename, typename> class F, typename I, typename O>
struct Evaluate <F <I, O> > {
    typedef O Type;
};

static_assert (std::is_same <
    Evaluate <Sum <Tuple <Value <int, 3>, Value <int, 4> > > >::Type,
    Tuple <Value <int, 7> >
>::value, "3 + 4 == 7");
static_assert (std::is_same <
    Evaluate <Product <Tuple <Value <int, 2>, Value <int, 5> > > >::Type,
    Tuple <Value <int, 10> >
>::value, "2 * 5 == 10");

熟悉 C++ 标准的人会立即指出 14.5.5/8:“特化的模板参数列表不应包含默认模板参数值。”,并附有戏弄脚注:“没有办法可以在其中使用它们。”。事实上,为任何现代编译器提供此代码都会在 Sum 上产生编译器错误。和 Product关于违反标准的模板特化。除了证明上述脚注缺乏作者的想象力外;我们已经为他们创建了一个有效的用例。

现在可以提出我的问题:有没有其他方法可以达到类似的效果 SumProduct保持未定义/不完整的类型,从而平凡地不可推断和不可构造,同时仍然承担执行操作的责任?欢迎提出任何建议。提前致谢。

最佳答案

元编程很复杂。它不是语言的设计特征,而是被发现的。因此,很难做对 - 所以人们想出了如何调用元函数的约定,以便为其他程序员提供指南以理解代码。最重要的约定之一是要获得元函数的结果,您可以这样做:

typename metafunc<some_args...>::type

您对写作的各种建议Sum根本不符合该约定,我认为即使是许多非常有经验的模板元程序员也很难理解您在做什么 - 甚至会依赖于对模板部分特化工作方式规则的更改。不过,改变这些规则根本不是一个令人信服的例子。让我提出更好的建议。

在元编程中,类型是一等公民。一切都只适用于类型。值和模板模板不是。他们充其量是笨重的。事实上,你需要写 Sum<int, 1, 2>糟透了。此外,当您必须允许具有不同数量参数的值或模板模板时,基本上不可能编写通用元函数。因此,让我们尝试将所有内容都保持为一种类型。

Boost.MPL 使用的概念之一是 metafunction class .我们可以对它进行一些 C++11 化处理,并称元函数类是如下所示的类:

struct C {
    template <typename... Args> // doesn't have to be variadic
    using apply = /* whatever */;
};

使用这个想法,我们可以说 EvaluateBinaryOperator看起来像:

template <typename Op, typename A1, typename A2>
struct EvaluateBinaryOperator {
    using type = typename Op::template apply<A1, A2>;
};

请注意,当一切都是类型时,它是多么干净!当然,调用 apply 的语法不及steller,但这很简单。事实上,我们可以归纳为:

template <typename Op, typename... Args>
struct EvaluateOperator {
    using type = typename Op::template apply<Args...>;
};

简单。现在让我们回到Sum .类型是一等公民,所以它不再接受值。它需要类型。但是我们仍然可以强制这些类型是具有相同类型的整型常量:

class Sum {
    template <typename, typename >
    struct impl;

    template <typename T, T a, T b>
    struct impl<std::integral_constant<T, a>,
                std::integral_constant<T, b>>
    {
        using type = std::integral_constant<T, a+b>;
    };

public:
    template <typename T, typename U>
    using apply = typename impl<T, U>::type;
};

这符合元函数类的模型,因此我们可以将它与EvaluateOperator 一起使用,例如:

std::cout << EvaluateOperator<Sum, 
                    std::integral_constant<int, 1>,
                    std::integral_constant<int, 2>
                    >::type::value << std::endl; // prints 3

实例化 impl使用两种不是同一基础类型的整数常量的类型会给您带来您想要的不完整类型错误。

使用元函数类还为您提供了柯里化(Currying)的优势。您不能真正从元函数“返回”一个类模板,但是您可以返回一个元函数类:

template <typename Op, typename... Args>
struct curry {
    struct type {
        template <typename... OtherArgs>
        using apply = typename EvaluateOperator<Op, Args..., OtherArgs...>::type;
    };
};

using Add1 = curry<Sum, std::integral_constant<int, 1>>::type;
std::cout << Add1::apply<
                 std::integral_constant<int, 5>
             >::type::value << std::endl; // prints 6

关于c++ - 如何通过未定义类型定义元函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31761965/

有关c++ - 如何通过未定义类型定义元函数?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  6. 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

  7. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  8. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  9. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  10. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

随机推荐