草庐IT

c++ - SFINAE - 如果更复杂的函数失败,则返回默认函数

coder 2024-02-03 原文

假设我编写了一个名为interpolate 的通用函数。它的签名是这样的:

template<typename T>
T interpolate(T a, T b, float c);

其中 a 和 b 是要插入的值,c 是 [0.0,1.0] 中的 float 。

如果 T 定义了 T operator*(float)T operator+(T),我希望它以某种方式表现(线性插值)。否则,它的行为会有所不同 - 任何 T 都可用(最近邻插值)。

我怎样才能实现这种行为?

例如:

interpolate<std::string>("hello","world!", 0.798); //uses nearest neighbor, as std::string does not have the necessary operators

interpolate<double>(42.0,128.0, 0.5);              //uses linear, as double has the needed operators

注意:这个问题不是关于这些插值方法的实现,而是如何使用模板来切换函数的行为。

最佳答案

这听起来像是标签分发的主要用例:

我们创建了两个不同的标签类来区分这两个用例

struct linear_tag {};
struct nn_tag {};

template <typename T>
T impl(T a, T b, float c, linear_tag) {
    // linear interpolation here
}

template <typename T>
T impl(T a, T b, float c, nn_tag) {
    // nearest neighbor interpolation here
}

现在,我们需要从T中找出标签类型:

template <typename T>
linear_tag tag_for(
    T* p,
    std::enable_if_t<std::is_same_v<T, decltype((*p + *p) * 0.5)>>* = nullptr
);
nn_tag tag_for(...); // Fallback

第一个重载仅存在于任何 T t 的情况下, 表达式 (t + t) * 0.5f返回另一个 T .1 第二个重载始终存在,但由于 C 风格的可变参数,除非第一个重载不匹配,否则永远不会使用它。

然后,我们可以通过创建适当的标签来分派(dispatch)到任一版本:

template <typename T>
T interpolate(T a, T b, float c) {
    return impl(a, b, c, decltype(tag_for(static_cast<T*>(nullptr))){});
}

在这里,decltype(tag_for(static_cast<T*>(nullptr)))给我们正确的标签类型(作为 tag_for 的正确重载的返回类型)。

您可以用很少的开销添加额外的标签类型,并在 enable_if_t 中测试任意复杂的条件。 .此特定版本仅适用于 C++17(因为 is_same_v),但您可以使用 typename std::enable_if<...>::type 轻松使其与 C++11 兼容。和 std::is_same<...>::value相反 - 它只是有点冗长。

1 这是您在问题中指定的内容 - 但它很危险!例如,如果您使用整数,您将使用最近邻插值,因为 *返回 float , 不是 int .您应该改为测试表达式 (*t + *t) * 0.5f返回可转换T 的东西使用诸如 std::is_constructible_v<T, decltype((*t + *t) * 0.5f)> 之类的测试


作为奖励,这里有一个 c++20基于概念的实现不再需要标签(如评论中简要提到的那样)。不幸的是,没有编译器支持 requires在这个层面上,当然标准草案总是会发生变化:

template <typename T>
concept LinearInterpolatable = requires(T a, T b, float c) {
    { a + b } -> T;
    { a * c } -> T;
};

template <LinearInterpolatable T>
T interpolate(T a, T b, float c)
{
    // Linear interpolation
}

template <typename T>
T interpolate(T a, T b, float c)
{
    // Nearest-neighbor interpolation
}

关于c++ - SFINAE - 如果更复杂的函数失败,则返回默认函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50429944/

有关c++ - SFINAE - 如果更复杂的函数失败,则返回默认函数的更多相关文章

  1. 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返

  2. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

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

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

  4. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  8. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  9. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  10. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

随机推荐