简体   繁体   中英

Why is the binary search function throwing an error?

I am trying to search for a long long int using the binary_search function, but the function is throwing an error which I cannot interpret. Could you help me with this problem?

I am using code :: blocks and C ++ 11.

Erro: ..error: no match for call to '(main()::<lambda(const Client&, const Client&)>) (const int&, Client&)'|

Code:

#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

class Client{
public:
    int id;
    std::string name;
    long long int number;

};

int main()
{
    long long int s_num; // Edited. It was a mistake when typing here in the post.

    std::vector<Client> vCli =
    {
        { 1, "Stive", 68020899020 },
        { 2, "Anna",  13389155032 },
        { 3, "Olly",  32911233288 },
    };

    std::sort(vCli.begin(), vCli.end(), [](const Client &c1, const Client &c2) { return c1.number < c2.number; });

    for (Client c : vCli) //print test
        std::cout << c.id << " - " << c.name << " - " << c.number << std::endl;


    s_num = 13389155032; // Anna's number

    bool yesNumber = std::binary_search( vCli.begin(), vCli.end(), s_num,
                               []( const Client &c1, const Client &c2)
                                {
                                    return c1.number > c2.number;
                                } );


    yesNumber ? std::cout << "\nClient found.\n" : std::cout << "\nClient NOT found.\n" ;

    return 0;
}

From cppreference :

The types Type1 and Type2 must be such that an object of type T can be implicitly converted to both Type1 and Type2

In your case, T=int and int can not be implicitly converted to Client , hence the error.

Your lambda implements the wrong comparison.

You're asking std::binary_search to find the integer s_num within a collection of Client s, but you're only telling it how to sort Client s.

It needs a function that compares Client s to long long int s.

Taking an educated guess at your requirements, that means:

bool yesNumber = std::binary_search( vCli.begin(), vCli.end(), s_num,
    [](const Client &c, const long long int val)
    {
        return c.number > val;
    }
 );

I'm actually not sure that this is sufficient, though; you may need to provide a functor that can compare Client s and long long int s in both directions (ie one with which you can swap the arguments). I'll leave that as an exercise to the reader, but I can tell you that a lambda isn't going to do the job.

You have sorted the vector in the opposite order as you tell it when you use std::binary_search which will make the search fail.

And as pointed out, you use a type not implicitly convertible to a Client .

Consider adding constructors:

Client(int Id, const std::string& Name, long long int Number) :
    id(Id), name(Name), number(Number) 
{}
Client(long long int x) : id{}, name{}, number(x) {}

And then provide the correct comparator ( < ) in the binary search:

long long int s_num = 13389155032;

bool yesNumber = std::binary_search( vCli.begin(), vCli.end(), s_num,
    [](const Client& c1, const Client c2) {
        return c1.number < c2.number;
    }
);

Alternatively, create a Client that you use for searching:

Client s_num{{}, {}, 13389155032};

bool yesNumber = std::binary_search(vCli.begin(), vCli.end(), s_num,
    [](const Client& c1, const Client& c2) { 
        return c1.number < c2.number; 
    }
);

You have a few options here. One would be to define a constructor that will let you implicitly create a Client from a long long int .

Another would be to define comparison between long long int and Client . The tricky point here is that you need to be prepared to handle expressions like either: long long < Client or Client < long long . There are a couple of ways you could do that. One would be to define a comparison class that has overloads for both directions:

struct cmp {
    bool operator()(long long int a, Client const &b) {
        return a < b.number;
    }

    bool operator()(Client const &a, long long int b) {
        return a.number < b;
    }
};

Then pass an instance of that in the call to binary_search:

bool yesNumber = std::binary_search( vCli.begin(), vCli.end(), s_num, cmp());

Another possibility would be to define a pair of getKey functions, one for a long long , and one for a Client :

class Client { 
// ...

    friend long long getKey(Client const &a) { return a.number; }
};

long long getKey(long long a) { return a; }

...then write your lambda like this:

[](auto const &a, auto const &b) { return getKey(a) < getKey(b); }

This way, whenever a comparison is done using the template, a type is assigned to each of a and b , and that type is used to select which overload of getKey will be called for that parameter. Kind of roundabout, but it does work.

Yet another possibility would be to define the comparison inside of your Client class itself:

class Client { 
// ...

    // will be used for sorting:
    bool operator<(Client const &other) const { return number < other.number; }

    // will be used for searching:
    bool operator<(long long val) const { return number < val; }
    friend operator<(long long val, Client const &c) { return val < c.number; }
};

In this case, we don't need to specify anything about comparison when we do the sorting or searching:

// Each of these will automatically use Client::operator<
std::sort(vCli.begin(), vCli.end());

bool yesNumber = std::binary_search(vCli.begin(), vCli.end(), s_num);

One final point: your use of the ternary operator is a bit unusual, to say the least. Typically you use it to yield one of two values, which is then used in an expression. If you just want to execute on of two expressions, depending on whether an expression is true or false, you'd normally use an if / else to do it. So the obvious choices would be either:

std::cout << (yesNumber ? "\nClient found.\n" : "\nClient NOT found.\n");

...or else:

if (yesNumber) {
    std::cout << "\nclient found.\n";
} else {
    std::cout << "\nClient NOT found.\n";
}

Note the parentheses in the first case--they're necessary because << has higher precedence than the ternary operator. Without them, the statement would be parsed as:

(std::cout << yesNumber) ? "\nClient found.\n" : "\nClient NOT found.\n";

So it would just write yesNumber out to cout , then use the return from cout to select between the found / NOT found strings, but not put that result to any use.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM