C ++中的比较技巧 [英] Comparison tricks in C++

查看:112
本文介绍了C ++中的比较技巧的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个类:

  class foo {
public:
int data;
};

现在我想为这个类添加一个方法,做一些比较,看看它的数据



当然,我可以写 if(data == num1 || data == num2 || data == num3 .....),但老实说,我感到恶心,当我写 data == 每次我比较它



因此,我希望我可以这样写:

 code> if(data等于这些中的一个(num1,num2,num3,num4,num5 ...))
return true;
else
return false;

我想实现这个语句,数据等于这些num1,num2,num3,num4,num5 ...)



这是我的方法:

  #include< stdarg.h> 
bool is_equal_to_one_of_these(int count,...){
int i;
bool equal = false;
va_list arg_ptr;
va_start(arg_prt,count);
for(int x = 0; x< count; x ++){
i = va_arg(arg_ptr,int);
if(i == data){
equal = true;
break;
}
}
va_end(arg_ptr);
return equal;
}

这段代码将为我完成这项工作。但是每次我使用这种方法,我都要计算参数并传递给它。



有没有人有更好的主意?

解决方案

简单的方法



$ b最简单的方法是编写一个成员函数$ c> in()周围 std :: find 使用一对迭代器来查找有问题的数据。我写了一个简单的模板< class It>

 的成员函数< class It> 
bool in(it first,It last)const
{
return std :: find(first,last,data)!= last;
}

如果您无权访问 foo ,您可以写签名的非成员函数 template< class T> bool in(foo const& std :: initializer_list< T>)等,并像下面这样调用:

  in(f,{1,2,3}); 



难的方式



完全超过了:只需再添加两个 public 重载:




  • begin() end调用上一个的 std :: initializer_list 相应的初始化器列表参数的迭代器。

  • 一个用于任意容器作为输入,将对私人重载进行少许标记分派 detail_in()帮助器:

    • 一个重载执行带有尾返回类型的SFINAE技巧如果所讨论的容器 c 不包含,将从重载集中删除的decltype(c.find(data),bool())成员函数 find(),并且返回 bool 否则(这是通过滥用< begin() $ c / c> c c>中的 end()



因为 detail_in() helper形成继承层次结构(很像标准的iterator标签),第一个重载将匹配关联容器 std :: set std :: unordered_set 和他们的多表兄弟。所有其他容器,包括C数组, std :: array std :: vector std :: list 将匹配第二个重载。

  #include< algorithm> 
#include< array>
#include< initializer_list>
#include< type_traits>
#include< iostream>
#include< set>
#include< unordered_set>
#include< vector>

class foo
{
public:
int data;

template< class It>
bool in(It first,It last)const
{
std :: cout< iterator overload:;
return std :: find(first,last,data)!= last;
}

template< class T>
bool in(std :: initializer_list< T> il)const
{
std :: cout< initializer_list overload:;
return in(begin(il),end(il));
}

模板< class Container>
bool in(Container const& c)const
{
std :: cout< container overload:;
return detail_in(c,associationative_container_tag {});
}

private:
struct sequence_container_tag {};
struct associative_container_tag:sequence_container_tag {};

template< class AssociativeContainer>
auto detail_in(AssociativeContainer const& c,associative_container_tag)const
- > decltype(c.find(data),bool())
{
std :: cout< associative overload:;
return c.find(data)!= end(c);
}

template< class SequenceContainer>
bool detail_in(SequenceContainer const& c,sequence_container_tag)const
{
std :: cout< sequence overload:;
using std :: begin;使用std :: end;
return in(begin(c),end(c));
}
};

int main()
{
foo f {1};
int a1 [] = {1,2,3};
int a2 [] = {2,3,4};

std :: cout<< f.in({1,2,3})<< \\\
;
std :: cout<< f.in({2,3,4})<< \\\
;

std :: cout<< f.in(std :: begin(a1),std :: end(a1))<< \\\
;
std :: cout<< f.in(std :: begin(a2),std :: end(a2))<< \\\
;

std :: cout<< f.in(a1)<< \\\
;
std :: cout<< f.in(a2) \\\
;

std :: cout<< f.in(std :: array< int,3> {1,2,3})& \\\
;
std :: cout<< f.in(std :: array< int,3> {2,3,4})< \\\
;

std :: cout<< f.in(std :: vector< int> {1,2,3,3})< \\\
;
std :: cout<< f.in(std :: vector< int> {2,3,4})< \\\
;

std :: cout<< f.in(std :: set< int> {1,2,3})< \\\
;
std :: cout<< f.in(std :: set< int> {2,3,4})< \\\
;

std :: cout<< f.in(std :: unordered_set< int> {1,2,3})< \\\
;
std :: cout<< f.in(std :: unordered_set< int> {2,3,4})<< \\\
;
}

活动示例 - 对于所有可能的容器 - 为这两个数字集打印1和0。 >

std :: initializer_list 重载的用例适用于小型集合的成员测试在调用代码中明确写出的数字。它具有 O(N)复杂性,但避免任何堆分配。



对于任何重型大集,您可以将数字存储在关联容器中,例如 std :: set 或其 multi_set unordered_set 表兄弟。这将在存储这些数字时转到堆,但是具有 O(log N)或甚至 O(1)查找复杂性。



但是如果你碰巧只有一个序列容器充满数字,你也可以把它扔到类中,它会很乐意计算你的成员资格 O(N)时间。


A class:

class foo{
public:
    int data;
};

Now I want to add a method to this class, to do some comparison, to see if its data is equal to one of given numbers.

Of course, I can write if(data==num1|| data == num2|| data ==num3.....), but honestly speaking, I feel sick when I write data == every time I compare it to a number.

So, I hope I would be able to write something like this:

if(data is equal to one of these(num1,num2,num3,num4,num5...))
    return true;
else
    return false;

I want to implement this statement, data is equal to one of these(num1, num2, num3, num4, num5...)

Here is my approach:

#include <stdarg.h>
bool is_equal_to_one_of_these(int count,...){
    int i;
    bool equal = false;
    va_list arg_ptr;
    va_start(arg_prt,count);
    for(int x=0;x<count;x++){
        i = va_arg(arg_ptr,int);
        if( i == data ){
            equal = true;
            break;
        }
    }
    va_end(arg_ptr);
    return equal;
}

This piece of code will do the job for me. But every time I use this method, I'll have to count the parameters and pass it in.

Does anyone have a better idea?

解决方案

The easy way

The simplest approach is to write a member function wrapper called in() around std::find with a pair of iterators to look for the data in question. I wrote a simple template<class It> in(It first, It last) member function for that

template<class It>
bool in(It first, It last) const
{
    return std::find(first, last, data) != last;
}

If you have no access to the source of foo, you can write a non-member functions of signature template<class T> bool in(foo const&, std::initializer_list<T>) etc., and call it like

in(f, {1, 2, 3 });

The hard way

But let's go completely overboard with that: just add two more public overloads:

  • one taking a std::initializer_list parameter that calls the previous one with the begin() and end() iterators of the corresponding initializer list argument.
  • one for an arbitrary container as input that will do a little tag dispatching to two more private overloads of a detail_in() helper:
    • one overload doing a SFINAE trick with trailing return type decltype(c.find(data), bool()) that will be removed from the overload set if the container c in question does not have a member function find(), and that returns bool otherwise (this is achieved by abusing the comma operator inside decltype)
    • one fallback overload that simply takes the begin() and end() iterators and delegates to the original in() taking two iterators

Because the tags for the detail_in() helper form an inheritance hierarchy (much like the standard iterator tags), the first overload will match for the associative containers std::set and std::unordered_set and their multi-cousins. All other containers, including C-arrays, std::array, std::vector and std::list, will match the second overload.

#include <algorithm>
#include <array>
#include <initializer_list>
#include <type_traits>
#include <iostream>
#include <set>
#include <unordered_set>
#include <vector>

class foo
{
public:
    int data;

    template<class It>
    bool in(It first, It last) const
    {
        std::cout << "iterator overload: ";
        return std::find(first, last, data) != last;
    }

    template<class T>
    bool in(std::initializer_list<T> il) const
    {
        std::cout << "initializer_list overload: ";
        return in(begin(il), end(il));
    }

    template<class Container>
    bool in(Container const& c) const 
    {
        std::cout << "container overload: ";
        return detail_in(c, associative_container_tag{});    
    }

private:
    struct sequence_container_tag {};
    struct associative_container_tag: sequence_container_tag {};

    template<class AssociativeContainer>
    auto detail_in(AssociativeContainer const& c, associative_container_tag) const 
    -> decltype(c.find(data), bool())
    {
        std::cout << "associative overload: ";
        return c.find(data) != end(c);    
    }

    template<class SequenceContainer> 
    bool detail_in(SequenceContainer const& c, sequence_container_tag) const
    {
        std::cout << "sequence overload: ";
        using std::begin; using std::end;
        return in(begin(c), end(c));    
    }
};

int main()
{
    foo f{1};
    int a1[] = { 1, 2, 3};
    int a2[] = { 2, 3, 4};

    std::cout << f.in({1, 2, 3}) << "\n";
    std::cout << f.in({2, 3, 4}) << "\n";

    std::cout << f.in(std::begin(a1), std::end(a1)) << "\n";
    std::cout << f.in(std::begin(a2), std::end(a2)) << "\n";

    std::cout << f.in(a1) << "\n";
    std::cout << f.in(a2) << "\n";

    std::cout << f.in(std::array<int, 3>{ 1, 2, 3 }) << "\n";
    std::cout << f.in(std::array<int, 3>{ 2, 3, 4 }) << "\n";

    std::cout << f.in(std::vector<int>{ 1, 2, 3 }) << "\n";
    std::cout << f.in(std::vector<int>{ 2, 3, 4 }) << "\n";

    std::cout << f.in(std::set<int>{ 1, 2, 3 }) << "\n";
    std::cout << f.in(std::set<int>{ 2, 3, 4 }) << "\n";

    std::cout << f.in(std::unordered_set<int>{ 1, 2, 3 }) << "\n";
    std::cout << f.in(std::unordered_set<int>{ 2, 3, 4 }) << "\n";    
}

Live Example that -for all possible containers- prints 1 and 0 for both number sets.

The use cases for the std::initializer_list overload are for member-ship testing for small sets of numbers that you write out explicitly in calling code. It has O(N) complexity but avoids any heap allocations.

For anything heavy-duty like membership testing of large sets, you could store the numbers in an associative container like std::set, or its multi_set or unordered_set cousins. This will go to the heap when storing these numbers, but has O(log N) or even O(1) lookup complexity.

But if you happen to have just a sequence container full of numbers around, you can also throw that to the class and it will happily compute membership for you in O(N) time.

这篇关于C ++中的比较技巧的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆