确定模板类型正确谓词的方法

发布于 2024-08-03 02:38:41 字数 1460 浏览 8 评论 0原文

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

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*,则即使 In2 也会调用 std::less是一个int*

当给定 ::operator< 两个参数时,编译器能够推断出正确的类型并且应用标准类型提升规则。但是,当选择要传递给函数的谓词时,没有机会发生这种情况。 是否有一些聪明的方法可以根据 InIn2std::less<> >?

编辑:

下面的例子说明了问题:

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__ 扩展...,但我也不喜欢这个想法。有什么方法可以以标准一致的方式获得净效果吗?

Suppose I have a function which looks like this:

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

I would like this function to call another function which accepts a predicate. My initial instinct was to do something like this:

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>());
}

But there is a problem, what if In and In2 are iterators to different types? For example, char* vs int*. Depending on which is In and which is In2 the predicate may be truncating values during its comparison. For example, if In is char* then std::less<char> will be called even if In2 is an int*.

When ::operator< is given two parameters, the compiler is able to deduce the correct type and the standard type promotion rules apply. However, when selecting a predicate to pass to a function, there is no oportunity to have this happen. Is there some clever way to figure out which version of std::less<> I want to pass based on In and In2?

EDIT:

The following example illustrates the problem:

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;

will output:

1
0

EDIT:

After thinking about it, what I would really like is to be able to do something like this:

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

I suppose I could use gcc's __typeof__ extension..., but I don't love that idea either. Any way to get that net effect in a standard conformant way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

大姐,你呐 2024-08-10 02:38:41

我似乎记得 boost 中有一个特性,但我很快就找不到它搜索。如果您不比我成功,您可以自己构建它,

template <typename T1, typename T2>
struct least_common_promotion;

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

但您必须指定相当多的明确的专业化。 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>());

I seemed to remember that there was a traits for this in boost, but I can't find it after a quick search. If you are no more successful than me, you can construct it yourself,

template <typename T1, typename T2>
struct least_common_promotion;

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

but you'll have to specify quite a few explicit specializations. The type traits library of boost can perhaps help you reduce their number.

Edit: I feel stupid, such kind of things are needed for operation (where the result type depend on the operands types), but not for predicates (where the result type is bool). You can simply write:

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>());
勿忘初心 2024-08-10 02:38:41

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

无论它们相同还是不同,都取决于您的例程的客户端是否满足算法的先决条件,您可以指定这些先决条件。例如,此处您可能要求 Invalue_typeIn2value_type 相同。如果这是正确的,那么该函数应该编译并且按照客户的期望是正确的。

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

但是,如果客户端违反了该前提条件(如上面提供的示例,其中 charint 不同),那么这将取决于客户端,而不是客户端你,纠正编译时错误。

至少可以说,确保您的算法有详细记录:)

If your requirements on the algorithm are such that In's value_type need not be the same as In2's value type, then I would leave the template parameters as you have them; otherwise they should be the same.

Whether they are the same or different it is up to the client of your routine to meet the prerequisites of the algorithm, which you are allowed to specify. For example, here you could require that the value_type of In be the same as the value_type of In2. If that holds true, then, the function should compile and be correct as the client expects.

In such a case, then, you can pass a std::less<T> instance of the value_type of either template type, and you should be fine.

However, if the client violates that precondition (as in the example you provide above where char is not the same as int), then it will be up to the client, not you, to correct the compile-time error.

Make sure your algorithm is well-documented, to say the least :)

与君绝 2024-08-10 02:38:41

以 SGI 的 std::equal 旧实现为例,STL 算法通过使用同一算法的两个版本来处理这种情况:一个使用内部 <编译器在编译时推导的运算符,以及采用用户定义的二进制谓词的运算符,以便用户可以使用他们喜欢的任何类型:(

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 代码取自 此处。)

Taking SGI's old implementation of std::equal as an example, STL algorithms handle this kind of situation by having two versions of the same algorithm: one that uses the intrinsic < operator which the compiler deduces at compile time, and one that takes a user-defined binary predicate so the user can use any types they'd prefer:

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;
}

(Note: Old SGI STL code taken from here.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文