实现可变的最小/最大功能

尼科斯(Nikos Athanasiou)

我正在实现可变参数的最小/最大函数。一个目标是利用编译时已知数量的参数并执行展开式评估(避免运行时循环)。代码的当前状态如下(表示min-max相似)

#include <iostream>  

using namespace std;

template<typename T>
T vmin(T val1, T val2)
{
    return val1 < val2 ? val1 : val2;
}

template<typename T, typename... Ts>
T vmin(T val1, T val2, Ts&&... vs)
{
    return val1 < val2 ?
        vmin(val1, std::forward<Ts>(vs)...) : 
            vmin(val2, std::forward<Ts>(vs)...);
}

int main()
{
    cout << vmin(3, 2, 1, 2, 5) << endl;    
    cout << vmin(3., 1.2, 1.3, 2., 5.2) << endl;
    return 0;
}

现在可以使用,但是我有一些问题/疑问

  1. 非可变参数过载有由价值接受它的参数。如果我尝试传递其他类型的ref,则会得到以下结果

    • 通用参考&&->编译错误
    • const引用const&->确定
    • 普通引用&->编译错误

    现在,我知道功能模板与模板混合在一起很奇怪,但是手头上是否有特定的专有技术?我应该选择哪种类型的参数?

  2. 不能充分扩展参数包吗?我是否真的需要将参数转发给递归调用?

  3. 将其包装在结构中并公开为静态成员函数时,是否可以更好地实现此功能。能专攻我的东西会给我买什么吗?

  4. 功能版本是否有更健壮/高效的实现/设计?(特别是我想知道一个constexpr版本是否适合模板元编程的效率)

Yakk-亚当·内夫罗蒙特

现场例子

这样可以完美地转发参数。它依赖RVO来获取返回值,因为无论输入类型如何,它都会返回值类型,因为common_type这样做是。

我实现了common_type推论,允许传入混合类型,并输出“预期”结果类型。

我们支持1个元素的最小值,因为它会使代码更加流畅。

#include <utility>
#include <type_traits>

template<typename T>
T vmin(T&&t)
{
  return std::forward<T>(t);
}

template<typename T0, typename T1, typename... Ts>
typename std::common_type<
  T0, T1, Ts...
>::type vmin(T0&& val1, T1&& val2, Ts&&... vs)
{
  if (val2 < val1)
    return vmin(val2, std::forward<Ts>(vs)...);
  else
    return vmin(val1, std::forward<Ts>(vs)...);
}


int main()
{
  std::cout << vmin(3, 2, 0.9, 2, 5) << std::endl;

  std::cout << vmin(3., 1.2, 1.3, 2., 5.2) << std::endl;

  return 0;
}

现在,尽管以上是一个完全可以接受的解决方案,但它并不理想。

该表达式((a<b)?a:b) = 7是合法的C ++,但vmin( a, b ) = 7不是,因为std::common_type decays是盲目的参数(由我认为是对它的过度反应所致,当在较旧的实现中使用两种值类型时,返回rvalue引用会导致这种情况std::common_type)。

简单地使用decltype( true?a:b )是很诱人的,但是它既会导致右值引用问题,又不支持common_type专业化(例如std::chrono)。所以我们都想使用common_type,也不想使用它。

其次,编写min不支持无关指针并且不让用户更改比较函数的函数似乎是错误的。

因此,下面是上述内容的更复杂版本。现场示例

#include <iostream>
#include <utility>
#include <type_traits>

namespace my_min {

  // a common_type that when fed lvalue references all of the same type, returns an lvalue reference all of the same type
  // however, it is smart enough to also understand common_type specializations.  This works around a quirk
  // in the standard, where (true?x:y) is an lvalue reference, while common_type< X, Y >::type is not.
  template<typename... Ts>
  struct my_common_type;

  template<typename T>
  struct my_common_type<T>{typedef T type;};

  template<typename T0, typename T1, typename... Ts>
  struct my_common_type<T0, T1, Ts...> {
    typedef typename std::common_type<T0, T1>::type std_type;
    // if the types are the same, don't change them, unlike what common_type does:
    typedef typename std::conditional< std::is_same< T0, T1 >::value,
      T0,
    std_type >::type working_type;
    // Careful!  We do NOT want to return an rvalue reference.  Just return T:
    typedef typename std::conditional<
      std::is_rvalue_reference< working_type >::value,
      typename std::decay< working_type >::type,
      working_type
    >::type common_type_for_first_two;
    // TODO: what about Base& and Derived&?  Returning a Base& might be the right thing to do.
    // on the other hand, that encourages silent slicing.  So maybe not.
    typedef typename my_common_type< common_type_for_first_two, Ts... >::type type;
  };
  template<typename... Ts>
  using my_common_type_t = typename my_common_type<Ts...>::type;
  // not that this returns a value type if t is an rvalue:
  template<typename Picker, typename T>
  T pick(Picker&& /*unused*/, T&&t)
  {
    return std::forward<T>(t);
  }
  // slight optimization would be to make Picker be forward-called at the actual 2-arg case, but I don't care:
  template<typename Picker, typename T0, typename T1, typename... Ts>
  my_common_type_t< T0, T1, Ts...> pick(Picker&& picker, T0&& val1, T1&& val2, Ts&&... vs)
  {
    // if picker doesn't prefer 2 over 1, use 1 -- stability!
    if (picker(val2, val1))
      return pick(std::forward<Picker>(pick), val2, std::forward<Ts>(vs)...);
    else
      return pick(std::forward<Picker>(pick), val1, std::forward<Ts>(vs)...);
  }

  // possibly replace with less<void> in C++1y?
  struct lesser {
    template<typename LHS, typename RHS>
    bool operator()( LHS&& lhs, RHS&& rhs ) const {
      return std::less< typename std::decay<my_common_type_t<LHS, RHS>>::type >()(
          std::forward<LHS>(lhs), std::forward<RHS>(rhs)
      );
    }
  };
  // simply forward to the picked_min function with a smart less than functor
  // note that we support unrelated pointers!
  template<typename... Ts>
  auto min( Ts&&... ts )->decltype( pick( lesser(), std::declval<Ts>()... ) )
  {
    return pick( lesser(), std::forward<Ts>(ts)... );
  }
}

int main()
{
  int x = 7;
  int y = 3;
  int z = -1;
  my_min::min(x, y, z) = 2;
  std::cout << x << "," << y << "," << z << "\n";
  std::cout << my_min::min(3, 2, 0.9, 2, 5) << std::endl;
  std::cout << my_min::min(3., 1.2, 1.3, 2., 5.2) << std::endl;
  return 0;
}

上述实现的不利之处在于,大多数类都不支持operator=(T const&)&&=delete-即,它们不会阻止将rvalue赋值给它,如果minnot中的一种类型,可能会导致意外情况基本类型呢。

附带说明:开始删除右值引用operator=的人员。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Visual Studio的最小和最大功能

来自分类Dev

实现自定义最大功能

来自分类Dev

下划线的最大功能实现

来自分类Dev

SVM的最大功能数量

来自分类Dev

SVM的最大功能数量

来自分类Dev

使用计数最大功能

来自分类Dev

最小/最大功能有两个可比

来自分类Dev

具有最大功能的std :: stack <int>?

来自分类Dev

使用最大功能而不进行分组

来自分类Dev

在宏SAS中使用最大功能

来自分类Dev

单个单元格中的最大功能数

来自分类Dev

键盘的最大功能键数是多少?

来自分类常见问题

新Set(arr1)的最大功能是什么?

来自分类Dev

PHP错误:达到最大功能嵌套级别'100',正在中止

来自分类Dev

如何修复laravel 5.2此错误“达到最大功能嵌套级别'100',正在中止!”?

来自分类Dev

查找具有最大功能值的列表的最佳组合

来自分类Dev

这个幼稚的解决方案的最大功能是什么?

来自分类Dev

新Set(arr1)的最大功能是什么?

来自分类Dev

Yii2错误:达到最大功能嵌套级别'100',正在中止

来自分类Dev

Symfony清除缓存和最大功能嵌套级别达到“ 100”

来自分类Dev

用PARI / GP进行质数除法的最大功效

来自分类Dev

为什么10 ^ 9942066是我可以计算而没有溢出的最大功效?

来自分类Dev

如何让while语句以小于x的最大功率工作?

来自分类Dev

在启动过程中,图形卡会消耗其最大功耗的100%吗?

来自分类Dev

实现最小功能

来自分类Dev

python3:lambda自动缩减功能可实现最大但不最小的功能

来自分类Dev

Excel大功能重复

来自分类Dev

方案中可变参数图功能的实现

来自分类Dev

方案中可变参数图功能的实现