【问题标题】:Way to determine proper predicate for templated types为模板类型确定正确谓词的方法
【发布时间】:2009-08-30 02:51:05
【问题描述】:

假设我有一个如下所示的函数:

template <class In, class In2>
void func(In first, In last, In2 first2);

我希望这个函数调用另一个接受谓词的函数。我最初的直觉是做这样的事情:

template <class In, class In2>
void func(In first, In last, In2 first2) {
    typedef typename std::iterator_traits<In>::value_type T;
    other_func(first, last, first2, std::less<T>());
}

但是有一个问题,如果InIn2 是不同类型的迭代器呢?例如,char*int*。根据哪个是In 和哪个是In2,谓词可能会在比较期间截断值。例如,如果Inchar*,那么即使In2int*,也会调用std::less&lt;char&gt;

::operator&lt; 被赋予两个参数时,编译器能够推断出正确的类型并应用标准类型提升规则。 However, when selecting a predicate to pass to a function, there is no oportunity to have this happen. 有没有一些聪明的方法可以根据InIn2 确定我想通过哪个版本的std::less&lt;&gt;

编辑:

下面的例子说明了这个问题:

unsigned int x = 0x80000000;
unsigned char y = 1;
std::cout << std::less<unsigned char>()(x, y) << std::endl;
std::cout << std::less<unsigned int>()(x, y) << std::endl;

将输出:

1
0

编辑

想了想,我真正想要的是能够做到这样的事情:

typedef typeof(T1() < T2()) T;
other_func(first, last, first2, std::less<T>());

我想我可以使用 gcc 的 __typeof__ 扩展...,但我也不喜欢这个想法。有什么方法可以以符合标准的方式获得这种净效果?

【问题讨论】:

    标签: c++ templates predicate


    【解决方案1】:

    我似乎记得在boost 中有一个特征,但我快速搜索后找不到它。如果你不比我成功,你可以自己建造,

    template <typename T1, typename T2>
    struct least_common_promotion;
    
    template <>
    struct least_common_promotion<short, int>
    {
        typedef int type;
    };
    

    但是您必须指定很多明确的专业化。 type traits 的 boost 库或许可以帮助你减少它们的数量。

    编辑:我觉得很愚蠢,操作需要这种东西(结果类型取决于操作数类型),而不是谓词(结果类型是bool)。你可以简单地写:

    template <class T1, T2>
    struct unhomogenous_less : public std::binary_function<T1, T2, bool>
    {
       bool operator()(T1 const& l, T2 const& r) const
       { return l < r; }
    };
    
    ...
    
    typedef typename std::iterator_traits<In>::value_type value_type_1;
    typedef typename std::iterator_traits<In2>::value_type value_type_2;
    other_func(first, last, first2, unhomogenous_less<value_type_1, value_type_2>());
    

    【讨论】:

    • 是的,到目前为止,非同质的少模板似乎是解决方案。事实上,我想知道为什么 std::less(或所有 binary_function 比较器)不采用 2 个模板参数。似乎这是使 std::less 在所有情况下都表现得像 operator
    【解决方案2】:

    如果您对算法的要求是Invalue_type 不必与In2 的值类型相同,那么我会将模板参数保留为您所拥有的;否则它们应该是相同的。

    它们是相同还是不同,取决于您的例程的客户端是否满足算法的先决条件,您可以指定这些先决条件。例如,在这里您可以要求Invalue_typeIn2value_type 相同。如果这是真的,那么该函数应该可以编译并且是正确的,就像客户期望的那样。

    在这种情况下,您可以传递任一模板类型的value_typestd::less&lt;T&gt; 实例,您应该没问题。

    但是,如果客户端违反了该前提条件(如您在上面提供的示例中,charint 不同),那么将由客户端而不是您来更正编译 -时间错误。

    至少可以说,确保您的算法有据可查:)

    【讨论】:

    • 但是算法接受不同但兼容类型的迭代器是合理的(就像在我的示例中,可以将 char 与 int 进行比较)。但是std::less&lt;char&gt;(some_int, some_char); 是一个错误,而::operator&lt;(some_int, some_char) 不是。
    • 您可以使用 Boost 的静态断言库等工具在编译时断言类型相同。
    • 我想让它们相同。基本上,我想要一个正确的方法来选择一个谓词,使它像::operator&lt; 一样起作用(如果可能的话,它将促进更小的类型)。
    • 如果一个实用模板根据两个 value_type 中的较大者为 less 函子选择 T 呢?
    • 我已经考虑过了,它适用于大多数情况......但也意味着实用程序类也可以为任何自定义类实现,即使它们有适当的操作符
    【解决方案3】:

    以 SGI 的旧实现 std::equal 为例,STL 算法通过具有相同算法的两个版本来处理这种情况:一个使用编译器在编译时推导出的内在 &lt; 运算符,另一个它采用用户定义的二进制谓词,因此用户可以使用他们喜欢的任何类型:

    template <class _InputIter1, class _InputIter2>
    inline bool equal(_InputIter1 __first1, _InputIter1 __last1,
                      _InputIter2 __first2) {
      __STL_REQUIRES(_InputIter1, _InputIterator);
      __STL_REQUIRES(_InputIter2, _InputIterator);
      __STL_REQUIRES(typename iterator_traits<_InputIter1>::value_type,
                     _EqualityComparable);
      __STL_REQUIRES(typename iterator_traits<_InputIter2>::value_type,
                     _EqualityComparable);
      for ( ; __first1 != __last1; ++__first1, ++__first2)
        if (*__first1 != *__first2)
          return false;
      return true;
    }
    
    template <class _InputIter1, class _InputIter2, class _BinaryPredicate>
    inline bool equal(_InputIter1 __first1, _InputIter1 __last1,
                      _InputIter2 __first2, _BinaryPredicate __binary_pred) {
      __STL_REQUIRES(_InputIter1, _InputIterator);
      __STL_REQUIRES(_InputIter2, _InputIterator);
      for ( ; __first1 != __last1; ++__first1, ++__first2)
        if (!__binary_pred(*__first1, *__first2))
          return false;
      return true;
    }
    

    (注:旧 SGI STL 代码取自 here。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-06
      • 1970-01-01
      • 2017-11-14
      • 1970-01-01
      相关资源
      最近更新 更多