Home > Mobile >  How to fix error c3848 or c2676 in c for this easy code
How to fix error c3848 or c2676 in c for this easy code

Time:04-02

I am a beginner of C , and I code what The Cherno teaches in his 100th video of the C series, showing at 16:50. But VS is always giving me error.

If without commenting the const part, VS gives me error c3848. After adding const, VS give me error c2676.

I have checked the way to use std::hash on cppreference, and searched for the error on Google, but get nothing. It's just "a little bit" too hard for a beginner like me.

Below is the code.

#include<iostream> 
#include<map> 
#include<unordered_map>

struct CityRecord
{
    std::string Name;
    uint64_t Population;
    double Latitude, Longtitude;
};

namespace std {

    template<>
    struct hash<CityRecord>
    {
        size_t operator()(const CityRecord& key) //const noexcept
        {
            return hash<std::string>()(key.Name);
        }

    };
}

int main()
{

    std::unordered_map<CityRecord, uint32_t> foundedMap;


    foundedMap[CityRecord{ "London", 500000, 2.4, 9.4 }] = 1850;


    uint32_t NewYorkYear = foundedMap[CityRecord{ "NY", 7000000, 2.4, 9.4 }];
}

As a beginner, I just want to know how to use the hash function in this case.

CodePudding user response:

You need to make the overloaded operator() for the specialization of hash for CityRecord a const member function as shown below. Additionally, we also need to overload operator== for CityRecord as shown below:

struct CityRecord
{
    std::string Name;
    uint64_t Population;
    double Latitude, Longtitude;
    //friend declaration for operator== not needed since we've a struct
    
};

//implement operator== 
bool operator==(const CityRecord &lhs, const CityRecord &rhs)
{
    return (lhs.Name == rhs.Name) && (lhs.Population == rhs.Population) && (lhs.Latitude ==rhs.Latitude) && (lhs.Longtitude == rhs.Longtitude);
}
namespace std {

    template<>
    struct hash<CityRecord>
    {
//-----------------------------------------------vvvvv-->added this const
        size_t operator()(const CityRecord& key) const
        {
            return hash<std::string>()(key.Name) ^ hash<uint64_t>()(key.Population) ^ hash<double>()(key.Latitude) ^ hash<double>()(key.Longtitude);
        }

    };
}

Working demo

CodePudding user response:

There is a much easier solution, without opening the std namespace ans pecializing the std::hash

If you look at the definition of the std::unordered_map in the CPP reference here, then you will read:

template<
    class Key,
    class T,
    class Hash = std::hash<Key>,
    class KeyEqual = std::equal_to<Key>,
    class Allocator = std::allocator< std::pair<const Key, T> >
> class unordered_map;

It is clear and normal, to hand in template parameters for the key-type and the value-type value. However, if the key-type if a custom type, like in your case, then you need to add additional functionality.

First, you need to add hash functionionality. If you read here about std::hash, then the only function the will be called is the "function call operator".

And this must be a "const" function, which will fix one of your problems.

And of course, you may add this function to your struct. That is completely OK. Please see in the example code below. With taht, you can give your own class as a template parameter for the hash functionality to the std::unordered_map. Cool.

If we look at the next template parameter of the std::unordered_map, then we will find std::equal_to. And if we read about this in cppreference, then we will find the following statement:

Function object for performing comparisons. Unless specialised, invokes operator== on type T.

So, we need to add a comparison operator == to your custom struct to satisfy requirements.

Please note: It is a good approach to encapsulate the methods operating on the data of classes within the class and not define them as free functions. Because only the methods of the class should work on the data of the class. If you would later change something in a class and have a free function doing work on the class members. So, please try to follow that approach.

Then, next, the comparison. So, we define the "operator ==" in your class and then have to compare element by element.

For easing up this task, there is a library function called std::tie. Please see here. This basically creates a std::tuple from the given parameters with the advantage, that all comparison functions are already defined and can be immediately reused.

By following the above described approach, the whole implementation will be much simpler.

Please see the below example code:

#include<iostream> 
#include<map> 
#include<unordered_map>
#include<tuple>

struct CityRecord
{
    std::string Name;
    uint64_t Population;
    double Latitude, Longtitude;

    // For std::equal_to
    bool operator == (const CityRecord& cr) const { return std::tie(Name, Population, Latitude, Longtitude) == std::tie(cr.Name, cr.Population, cr.Latitude, cr.Longtitude); }
    // For hashing
    size_t operator()(const CityRecord& key) const { return std::hash<std::string>{}(key.Name); }
};

int main() {

    // Definition of the unordered_map
    std::unordered_map<CityRecord, uint32_t, CityRecord> foundedMap;

    // Adding data
    foundedMap[CityRecord{ "London", 500000, 2.4, 9.4 }] = 1850;
    uint32_t NewYorkYear = foundedMap[CityRecord{ "NY", 7000000, 2.4, 9.4 }];
}
  • Related