0

My question is why does the following code work

hash<const char*> PassHash;

cout << PassHash("Hello World");

But this code wont compile.

hash<const char*> PassHash;
string password;
password = "Hello World";

cout << PassHash(password);

In Code Blocks I get this error

error: no match for call to '(__gnu_cxx::hash<const char*>) (std::string&)'|
ten ben
  • 11
  • 4
  • 4
    Because a `std::string` is not a `const char*`, and there are no applicable conversion operators. Use `password.c_str()`. [Related conversion question](https://stackoverflow.com/questions/347949/how-to-convert-a-stdstring-to-const-char-or-char). – WhozCraig Mar 02 '17 at 04:23
  • 1
    Because a `string` isn't a `const char *`... Why does `cout << PassHash(42);` not work? – user253751 Mar 02 '17 at 04:23
  • ps if you are planning to use this for an actual password - note that std::hash doesn't necessarily produce the same output for the same input the next time the program is run. – Martin Beckett Mar 02 '17 at 04:57
  • "_note that std::hash doesn't necessarily produce the same output for the same input the next time the program is run_" ... is this actually true? I would appreciate a link to some reference. – zett42 Mar 02 '17 at 09:23
  • @Martin "note that std::hash doesn't necessarily produce the same output for the same input the next time the program is run" ... is this actually true? I would appreciate a link to some reference. – zett42 Mar 02 '17 at 10:31
  • @zett42 http://en.cppreference.com/w/cpp/utility/hash or your copy of the c++14 std – Martin Beckett Mar 02 '17 at 13:45
  • Thanks, I somehow missed the C++14 note. Citing: "_Hash functions are only required to produce the same result for the same input within a single execution of a program; this allows salted hashes that prevent collision DoS attacks._" – zett42 Mar 02 '17 at 14:44
  • @MartinBeckett Is there a hashing function of some kind I can use for a password? Hopefully one that produces the same output even on multiple executions – ten ben Mar 02 '17 at 15:14
  • @tenben Use a secure hashing function that is designed for the purpose sha-1 (or if security is a real concern one of the more secure replacements) – Martin Beckett Mar 02 '17 at 17:31

2 Answers2

4

std::hash has a definition similar to the following

template<typename T>
struct hash {
  size_t operator()(const T& value) const {
    ...
  }
}

So it's trivial that std::hash<const char*> template instantiation defines an operator() which accepts a const char*, but you are passing a std::string which is a different type.

Just use directly std::string for your password variable and std::hash<std::string> instead.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • You can use `const char*` as argument for `std::hash` too because it will implicitly be converted to std::string. The more important difference between `std::hash` and `std::hash` is that the former only hashes the pointer whereas the latter hashes the entire string. – zett42 Mar 02 '17 at 09:13
  • @zett42: which is a substantial difference, using `std::hash` can lead to weird obscure behavior if you don't know what you are doing. For example two `const char*` representing the same sequence but allocated on heap at different locations. – Jack Mar 02 '17 at 14:35
1

There is no implicit conversion from std::string to const char* which would be required to make your example work.

You may call string::c_str() to explicitly do this "conversion"...

hash<const char*> PassHash;
string password;
password = "Hello World";

cout << PassHash(password.c_str());

... but this will only calculate the hash of the string pointer because there is no specialization for hash<const char*>! So this only matches the generic pointer specialization hash<T*>.

What you propably really want is hash over the entire character array of the string, so if one character of the string changes, you (most likely) get a different hash value.

For this you could use the hash<std::string> specialization. This works for both const char*and std::string arguments as expected, because std::string has a conversion constructor that takes a const char*.

Example:

const char* password1 = "Hello World";
string password2 = "Hello World";

hash<const char*> charPtrHasher;

// This only calculates a hash from the value of the pointer, not from 
// the actual string data! This is why you get a different hash for each.
cout << "Test 1:\n";
cout << charPtrHasher(password1) << endl << charPtrHasher(password2.c_str()) << endl;

hash<std::string> stringHasher;

// This correctly calculates the hash over all characters of the string!
cout << "\nTest 2:\n";
cout << stringHasher(password1) << endl << stringHasher(password2) << endl;

Live Demo: http://coliru.stacked-crooked.com/a/047c099f5dcff948

zett42
  • 25,437
  • 3
  • 35
  • 72
  • To the editor of my post... what's wrong about `std::string::data()`? I think it adds consistency (e. g. compare to `std::vector::data()`). – zett42 Mar 02 '17 at 08:54
  • You propably refer to the [reference](http://en.cppreference.com/w/cpp/string/basic_string/data): "_The returned array is not required to be null-terminated._" ... but for `hash` it doesn't make a difference because it just hashes the pointer anyway (there is no specialization for C strings). – zett42 Mar 02 '17 at 08:58