std::hash specialisation for my own class and use it inside the class
I define a class Foo
, and would like to have a public member function that takes std::unordered_set<Foo>
as parameters type.
To be able to use std::unordered_set<Foo>
, I have to specialize std::hash<Foo>
in namespace std.
That's ok if I do not try to use std::unordered_set<Foo>
as paramter type in Foo
member functions.
However, once I want to use std::unordered_set<Foo>
as parameter type in Foo
member functions, I Have a problem to define the specialization std::hash<Foo>
.
If I do it after the Foo
declaration, there is an error on Foo
declaration because std::hash<Foo>
is not defined. It a move std::hash<Foo>
definition before, it does not work either because now Foo
is unknown. Forward declaration of Foo
does not work in such situation.
Any ideas how to resolve this ?
Here is an example of such a class
class Foo
{
public:
std::unordered_set<Foo>::iterator findClosest(std::unordered_set<Foo> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const
{
return bar.hashValue();
}
};
}
Using answers below, here is the code I finally use (I need to place some MY_EXPORT_MACRO because there is dlls):
In file Foo.h
class Foo;
namespace std
{
template <>
struct MY_EXPORT_MACRO hash<Foo>
{
size_t operator()(Foo const &bar) const;
};
}
class MY_EXPORT_MACRO Foo
{
public:
Foo const *findClosest(std::unordered_set<Foo> const &others);
size_t hashValue() const
{
return std::hash<int>()(m_Member);
}
bool operator==(const platypus::Segment2D &other) const
{
return m_Member == other.m_Member;
}
private:
int m_Member;
};
In file Foo.cpp
size_t std::hash<Foo>::operator()(Foo const &bar) const
{
return bar.hashValue();
}
Foo const *Foo::findClosest(std::unordered_set<Foo> const &others)
{
Foo const *closest = nullptr;
std::unordered_set<Foo>::const_iterator closestIt =
std::min_element(std::begin(others), std::end(others), [this](Foo const &lhs, Foo const &rhs) {
return std::abs(this->m_Member - lhs.m_Member) < std::abs(this->m_Member - rhs.m_Member);
});
if (closestIt != std::end(others))
{
closest = &(*closestIt);
}
return closest;
}
c++ c++11
add a comment |
I define a class Foo
, and would like to have a public member function that takes std::unordered_set<Foo>
as parameters type.
To be able to use std::unordered_set<Foo>
, I have to specialize std::hash<Foo>
in namespace std.
That's ok if I do not try to use std::unordered_set<Foo>
as paramter type in Foo
member functions.
However, once I want to use std::unordered_set<Foo>
as parameter type in Foo
member functions, I Have a problem to define the specialization std::hash<Foo>
.
If I do it after the Foo
declaration, there is an error on Foo
declaration because std::hash<Foo>
is not defined. It a move std::hash<Foo>
definition before, it does not work either because now Foo
is unknown. Forward declaration of Foo
does not work in such situation.
Any ideas how to resolve this ?
Here is an example of such a class
class Foo
{
public:
std::unordered_set<Foo>::iterator findClosest(std::unordered_set<Foo> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const
{
return bar.hashValue();
}
};
}
Using answers below, here is the code I finally use (I need to place some MY_EXPORT_MACRO because there is dlls):
In file Foo.h
class Foo;
namespace std
{
template <>
struct MY_EXPORT_MACRO hash<Foo>
{
size_t operator()(Foo const &bar) const;
};
}
class MY_EXPORT_MACRO Foo
{
public:
Foo const *findClosest(std::unordered_set<Foo> const &others);
size_t hashValue() const
{
return std::hash<int>()(m_Member);
}
bool operator==(const platypus::Segment2D &other) const
{
return m_Member == other.m_Member;
}
private:
int m_Member;
};
In file Foo.cpp
size_t std::hash<Foo>::operator()(Foo const &bar) const
{
return bar.hashValue();
}
Foo const *Foo::findClosest(std::unordered_set<Foo> const &others)
{
Foo const *closest = nullptr;
std::unordered_set<Foo>::const_iterator closestIt =
std::min_element(std::begin(others), std::end(others), [this](Foo const &lhs, Foo const &rhs) {
return std::abs(this->m_Member - lhs.m_Member) < std::abs(this->m_Member - rhs.m_Member);
});
if (closestIt != std::end(others))
{
closest = &(*closestIt);
}
return closest;
}
c++ c++11
MSVC in VS 2015 does not implement two-phase lookup correctly, so certain templated things working (or not) when they don't (or do) in gcc/clang is basically coincidental. If you're writing new code, you should not rely on this, as it will eventually break.
– Max Langhof
Nov 16 '18 at 10:07
Yes, I just tried in my real case, and it does no work. So I will fallback to the pointer version
– Azias
Nov 16 '18 at 10:19
Actually I am stuck because I cannot use to Pointer version neither, because it is not possible to get aFoo*
from astd::unordered_set<Foo>::iterator
– Azias
Nov 16 '18 at 10:30
What's wrong with&*myIt
(aside from syntactic awkwardness)?
– Max Langhof
Nov 16 '18 at 10:32
my bad, actually I have to returnFoo const *
and not aFoo *
, that's why I had problem with the Pointer version.
– Azias
Nov 16 '18 at 10:40
add a comment |
I define a class Foo
, and would like to have a public member function that takes std::unordered_set<Foo>
as parameters type.
To be able to use std::unordered_set<Foo>
, I have to specialize std::hash<Foo>
in namespace std.
That's ok if I do not try to use std::unordered_set<Foo>
as paramter type in Foo
member functions.
However, once I want to use std::unordered_set<Foo>
as parameter type in Foo
member functions, I Have a problem to define the specialization std::hash<Foo>
.
If I do it after the Foo
declaration, there is an error on Foo
declaration because std::hash<Foo>
is not defined. It a move std::hash<Foo>
definition before, it does not work either because now Foo
is unknown. Forward declaration of Foo
does not work in such situation.
Any ideas how to resolve this ?
Here is an example of such a class
class Foo
{
public:
std::unordered_set<Foo>::iterator findClosest(std::unordered_set<Foo> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const
{
return bar.hashValue();
}
};
}
Using answers below, here is the code I finally use (I need to place some MY_EXPORT_MACRO because there is dlls):
In file Foo.h
class Foo;
namespace std
{
template <>
struct MY_EXPORT_MACRO hash<Foo>
{
size_t operator()(Foo const &bar) const;
};
}
class MY_EXPORT_MACRO Foo
{
public:
Foo const *findClosest(std::unordered_set<Foo> const &others);
size_t hashValue() const
{
return std::hash<int>()(m_Member);
}
bool operator==(const platypus::Segment2D &other) const
{
return m_Member == other.m_Member;
}
private:
int m_Member;
};
In file Foo.cpp
size_t std::hash<Foo>::operator()(Foo const &bar) const
{
return bar.hashValue();
}
Foo const *Foo::findClosest(std::unordered_set<Foo> const &others)
{
Foo const *closest = nullptr;
std::unordered_set<Foo>::const_iterator closestIt =
std::min_element(std::begin(others), std::end(others), [this](Foo const &lhs, Foo const &rhs) {
return std::abs(this->m_Member - lhs.m_Member) < std::abs(this->m_Member - rhs.m_Member);
});
if (closestIt != std::end(others))
{
closest = &(*closestIt);
}
return closest;
}
c++ c++11
I define a class Foo
, and would like to have a public member function that takes std::unordered_set<Foo>
as parameters type.
To be able to use std::unordered_set<Foo>
, I have to specialize std::hash<Foo>
in namespace std.
That's ok if I do not try to use std::unordered_set<Foo>
as paramter type in Foo
member functions.
However, once I want to use std::unordered_set<Foo>
as parameter type in Foo
member functions, I Have a problem to define the specialization std::hash<Foo>
.
If I do it after the Foo
declaration, there is an error on Foo
declaration because std::hash<Foo>
is not defined. It a move std::hash<Foo>
definition before, it does not work either because now Foo
is unknown. Forward declaration of Foo
does not work in such situation.
Any ideas how to resolve this ?
Here is an example of such a class
class Foo
{
public:
std::unordered_set<Foo>::iterator findClosest(std::unordered_set<Foo> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const
{
return bar.hashValue();
}
};
}
Using answers below, here is the code I finally use (I need to place some MY_EXPORT_MACRO because there is dlls):
In file Foo.h
class Foo;
namespace std
{
template <>
struct MY_EXPORT_MACRO hash<Foo>
{
size_t operator()(Foo const &bar) const;
};
}
class MY_EXPORT_MACRO Foo
{
public:
Foo const *findClosest(std::unordered_set<Foo> const &others);
size_t hashValue() const
{
return std::hash<int>()(m_Member);
}
bool operator==(const platypus::Segment2D &other) const
{
return m_Member == other.m_Member;
}
private:
int m_Member;
};
In file Foo.cpp
size_t std::hash<Foo>::operator()(Foo const &bar) const
{
return bar.hashValue();
}
Foo const *Foo::findClosest(std::unordered_set<Foo> const &others)
{
Foo const *closest = nullptr;
std::unordered_set<Foo>::const_iterator closestIt =
std::min_element(std::begin(others), std::end(others), [this](Foo const &lhs, Foo const &rhs) {
return std::abs(this->m_Member - lhs.m_Member) < std::abs(this->m_Member - rhs.m_Member);
});
if (closestIt != std::end(others))
{
closest = &(*closestIt);
}
return closest;
}
c++ c++11
c++ c++11
edited Nov 20 '18 at 13:12
Azias
asked Nov 16 '18 at 8:27
AziasAzias
10918
10918
MSVC in VS 2015 does not implement two-phase lookup correctly, so certain templated things working (or not) when they don't (or do) in gcc/clang is basically coincidental. If you're writing new code, you should not rely on this, as it will eventually break.
– Max Langhof
Nov 16 '18 at 10:07
Yes, I just tried in my real case, and it does no work. So I will fallback to the pointer version
– Azias
Nov 16 '18 at 10:19
Actually I am stuck because I cannot use to Pointer version neither, because it is not possible to get aFoo*
from astd::unordered_set<Foo>::iterator
– Azias
Nov 16 '18 at 10:30
What's wrong with&*myIt
(aside from syntactic awkwardness)?
– Max Langhof
Nov 16 '18 at 10:32
my bad, actually I have to returnFoo const *
and not aFoo *
, that's why I had problem with the Pointer version.
– Azias
Nov 16 '18 at 10:40
add a comment |
MSVC in VS 2015 does not implement two-phase lookup correctly, so certain templated things working (or not) when they don't (or do) in gcc/clang is basically coincidental. If you're writing new code, you should not rely on this, as it will eventually break.
– Max Langhof
Nov 16 '18 at 10:07
Yes, I just tried in my real case, and it does no work. So I will fallback to the pointer version
– Azias
Nov 16 '18 at 10:19
Actually I am stuck because I cannot use to Pointer version neither, because it is not possible to get aFoo*
from astd::unordered_set<Foo>::iterator
– Azias
Nov 16 '18 at 10:30
What's wrong with&*myIt
(aside from syntactic awkwardness)?
– Max Langhof
Nov 16 '18 at 10:32
my bad, actually I have to returnFoo const *
and not aFoo *
, that's why I had problem with the Pointer version.
– Azias
Nov 16 '18 at 10:40
MSVC in VS 2015 does not implement two-phase lookup correctly, so certain templated things working (or not) when they don't (or do) in gcc/clang is basically coincidental. If you're writing new code, you should not rely on this, as it will eventually break.
– Max Langhof
Nov 16 '18 at 10:07
MSVC in VS 2015 does not implement two-phase lookup correctly, so certain templated things working (or not) when they don't (or do) in gcc/clang is basically coincidental. If you're writing new code, you should not rely on this, as it will eventually break.
– Max Langhof
Nov 16 '18 at 10:07
Yes, I just tried in my real case, and it does no work. So I will fallback to the pointer version
– Azias
Nov 16 '18 at 10:19
Yes, I just tried in my real case, and it does no work. So I will fallback to the pointer version
– Azias
Nov 16 '18 at 10:19
Actually I am stuck because I cannot use to Pointer version neither, because it is not possible to get a
Foo*
from a std::unordered_set<Foo>::iterator
– Azias
Nov 16 '18 at 10:30
Actually I am stuck because I cannot use to Pointer version neither, because it is not possible to get a
Foo*
from a std::unordered_set<Foo>::iterator
– Azias
Nov 16 '18 at 10:30
What's wrong with
&*myIt
(aside from syntactic awkwardness)?– Max Langhof
Nov 16 '18 at 10:32
What's wrong with
&*myIt
(aside from syntactic awkwardness)?– Max Langhof
Nov 16 '18 at 10:32
my bad, actually I have to return
Foo const *
and not a Foo *
, that's why I had problem with the Pointer version.– Azias
Nov 16 '18 at 10:40
my bad, actually I have to return
Foo const *
and not a Foo *
, that's why I had problem with the Pointer version.– Azias
Nov 16 '18 at 10:40
add a comment |
4 Answers
4
active
oldest
votes
The real problem in your example is that you want to use std::unordered_set<Foo>::iterator
as return type. This requires that unordered_set<Foo>
is fully instantiated, which requires Foo
(and std::hash<Foo>
) to be a complete class. But Foo
is only a complete class at the end of its definition. Note that this problem does not exist with the by-reference function parameter, where the compiler does not have to fully instantiate the referenced class.
As originally suggested by @MrTux, you can fix everything else with forward declarations:
class Foo;
template<>
struct std::hash<Foo>;
If you return a Foo*
instead, everything works:
Demo
Whether that works for your design is another question.
I should note that you can have std::hash<Foo>
be fully defined before Foo
's definition:
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo { /* ... */ };
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
That would allow you to return e.g. std::unordered_set<Foo>
from methods in Foo
, but it still does not fix the core issue of Foo
being incomplete (and thus std::unordered_set<Foo>::iterator
being unavailable) during its own definition.
Thank you, it effectively works with forward declaration if change the signature offindClosest
toFoo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.
– Azias
Nov 16 '18 at 9:20
2
You can return an iterator by writingauto findClosest(...) { return std::end(...); }
Like in here
– Julian vD
Nov 16 '18 at 9:25
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
add a comment |
I see that you tagged the Question as C++11 (so we cannot rely on type deduction for the return type), so here is a solution that returns a const_iterator
(others
is const):
#include <unordered_set>
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo
{
public:
template <class Hash>
typename std::unordered_set<Foo, Hash>::const_iterator findClosest(std::unordered_set<Foo, Hash> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
int main()
{
Foo f;
std::unordered_set<Foo> fs;
f.findClosest(fs);
}
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
@Azias It actually makes no difference because bothiterator
andconst_iterator
are const iterators (and may be the same type) forstd::set
andstd::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".
– Max Langhof
Nov 16 '18 at 9:58
add a comment |
You might find the code is clearer and easier to maintain if you move the concerns of hashing Foo and finding closest distance into free functions.
Here's an example using boost::hash
protocols (which I find to be very helpful)
#include <unordered_set>
#include <boost/functional/hash.hpp>
class Foo
{
public:
// return a tuple of immutable values which should be hashed in order to
// compute this object's hash value
auto hashy_stuff() const // -> std::tuple<const int&> // if you are < c++14
{
return std::tie(m_Member);
}
private:
int m_Member;
};
// implement the boost::hash hash_value protocol in terms of hashy_stuff
// (we win because now it's DRY and works with all boost hash functions)
auto hash_value(Foo const& foo) -> std::size_t { return boost::hash_value(foo.hashy_stuff()); }
// implement std::hash in terms of boost::hash (again, DRY because it automatically
// uses hash_value())
namespace std { template<> struct hash<Foo> : ::boost::hash<Foo> {}; }
// implement findClosest as a free function. Its concerns are tangential to
// the Foo-ness of a Foo.
std::unordered_set<Foo>::const_iterator
findClosest(Foo const& /*foo*/, std::unordered_set<Foo> const &others)
{
// implementation here in terms of public interface
return std::end(others);
}
extern auto make_foos() -> std::unordered_set<Foo>;
extern auto make_foo() -> Foo;
bool test()
{
auto foos = make_foos();
auto foo = make_foo();
auto i = findClosest(foo, foos);
return i != end(foos);
}
add a comment |
Instead of specializing template in std
namespace you can try below method if you like that.
#include <iostream>
#include <unordered_set>
struct FooHash;
class Foo {
public:
Foo(){
}
~Foo(){
}
friend FooHash;
private:
std::string m_str;
};
struct FooHash
{
std::size_t operator()(Foo const& f) const noexcept{
return std::hash<std::string>{}(f.m_str);
}
};
int main(){
std::unordered_set<Foo,FooHash> fSet;
return 0;
}
Live Code
This completely ignores the core of the question: Usingstd::unordered_set<Foo>
in the interface ofFoo
.
– Max Langhof
Nov 16 '18 at 8:58
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53334015%2fstdhash-specialisation-for-my-own-class-and-use-it-inside-the-class%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
The real problem in your example is that you want to use std::unordered_set<Foo>::iterator
as return type. This requires that unordered_set<Foo>
is fully instantiated, which requires Foo
(and std::hash<Foo>
) to be a complete class. But Foo
is only a complete class at the end of its definition. Note that this problem does not exist with the by-reference function parameter, where the compiler does not have to fully instantiate the referenced class.
As originally suggested by @MrTux, you can fix everything else with forward declarations:
class Foo;
template<>
struct std::hash<Foo>;
If you return a Foo*
instead, everything works:
Demo
Whether that works for your design is another question.
I should note that you can have std::hash<Foo>
be fully defined before Foo
's definition:
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo { /* ... */ };
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
That would allow you to return e.g. std::unordered_set<Foo>
from methods in Foo
, but it still does not fix the core issue of Foo
being incomplete (and thus std::unordered_set<Foo>::iterator
being unavailable) during its own definition.
Thank you, it effectively works with forward declaration if change the signature offindClosest
toFoo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.
– Azias
Nov 16 '18 at 9:20
2
You can return an iterator by writingauto findClosest(...) { return std::end(...); }
Like in here
– Julian vD
Nov 16 '18 at 9:25
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
add a comment |
The real problem in your example is that you want to use std::unordered_set<Foo>::iterator
as return type. This requires that unordered_set<Foo>
is fully instantiated, which requires Foo
(and std::hash<Foo>
) to be a complete class. But Foo
is only a complete class at the end of its definition. Note that this problem does not exist with the by-reference function parameter, where the compiler does not have to fully instantiate the referenced class.
As originally suggested by @MrTux, you can fix everything else with forward declarations:
class Foo;
template<>
struct std::hash<Foo>;
If you return a Foo*
instead, everything works:
Demo
Whether that works for your design is another question.
I should note that you can have std::hash<Foo>
be fully defined before Foo
's definition:
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo { /* ... */ };
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
That would allow you to return e.g. std::unordered_set<Foo>
from methods in Foo
, but it still does not fix the core issue of Foo
being incomplete (and thus std::unordered_set<Foo>::iterator
being unavailable) during its own definition.
Thank you, it effectively works with forward declaration if change the signature offindClosest
toFoo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.
– Azias
Nov 16 '18 at 9:20
2
You can return an iterator by writingauto findClosest(...) { return std::end(...); }
Like in here
– Julian vD
Nov 16 '18 at 9:25
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
add a comment |
The real problem in your example is that you want to use std::unordered_set<Foo>::iterator
as return type. This requires that unordered_set<Foo>
is fully instantiated, which requires Foo
(and std::hash<Foo>
) to be a complete class. But Foo
is only a complete class at the end of its definition. Note that this problem does not exist with the by-reference function parameter, where the compiler does not have to fully instantiate the referenced class.
As originally suggested by @MrTux, you can fix everything else with forward declarations:
class Foo;
template<>
struct std::hash<Foo>;
If you return a Foo*
instead, everything works:
Demo
Whether that works for your design is another question.
I should note that you can have std::hash<Foo>
be fully defined before Foo
's definition:
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo { /* ... */ };
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
That would allow you to return e.g. std::unordered_set<Foo>
from methods in Foo
, but it still does not fix the core issue of Foo
being incomplete (and thus std::unordered_set<Foo>::iterator
being unavailable) during its own definition.
The real problem in your example is that you want to use std::unordered_set<Foo>::iterator
as return type. This requires that unordered_set<Foo>
is fully instantiated, which requires Foo
(and std::hash<Foo>
) to be a complete class. But Foo
is only a complete class at the end of its definition. Note that this problem does not exist with the by-reference function parameter, where the compiler does not have to fully instantiate the referenced class.
As originally suggested by @MrTux, you can fix everything else with forward declarations:
class Foo;
template<>
struct std::hash<Foo>;
If you return a Foo*
instead, everything works:
Demo
Whether that works for your design is another question.
I should note that you can have std::hash<Foo>
be fully defined before Foo
's definition:
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo { /* ... */ };
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
That would allow you to return e.g. std::unordered_set<Foo>
from methods in Foo
, but it still does not fix the core issue of Foo
being incomplete (and thus std::unordered_set<Foo>::iterator
being unavailable) during its own definition.
edited Nov 16 '18 at 9:13
answered Nov 16 '18 at 8:57
Max LanghofMax Langhof
9,1911537
9,1911537
Thank you, it effectively works with forward declaration if change the signature offindClosest
toFoo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.
– Azias
Nov 16 '18 at 9:20
2
You can return an iterator by writingauto findClosest(...) { return std::end(...); }
Like in here
– Julian vD
Nov 16 '18 at 9:25
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
add a comment |
Thank you, it effectively works with forward declaration if change the signature offindClosest
toFoo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.
– Azias
Nov 16 '18 at 9:20
2
You can return an iterator by writingauto findClosest(...) { return std::end(...); }
Like in here
– Julian vD
Nov 16 '18 at 9:25
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
Thank you, it effectively works with forward declaration if change the signature of
findClosest
to Foo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.– Azias
Nov 16 '18 at 9:20
Thank you, it effectively works with forward declaration if change the signature of
findClosest
to Foo* findClosest(std::unordered_set<Foo> const &others)
. So it is an interesting solution for me. However if someone had a solution to be able to return an iterator (because it seems more clean for me), it would be perfect.– Azias
Nov 16 '18 at 9:20
2
2
You can return an iterator by writing
auto findClosest(...) { return std::end(...); }
Like in here– Julian vD
Nov 16 '18 at 9:25
You can return an iterator by writing
auto findClosest(...) { return std::end(...); }
Like in here– Julian vD
Nov 16 '18 at 9:25
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Julian actually it does not work in my real case, because class Foo and its use are in different dlls, and I get an error "a function that returns 'auto' cannot be used before it is defined". There is solutions for that but I do not want to deal with.
– Azias
Nov 16 '18 at 10:35
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
@Azias Well, in the background auto is somewhat like a template, the easiest solution would to just put the definition of findClosest in the header file. That way at the moment you include Foo, findClosest is defined. That's why many, if not all, of the standard containers are simply put in header files.
– Julian vD
Nov 16 '18 at 11:16
add a comment |
I see that you tagged the Question as C++11 (so we cannot rely on type deduction for the return type), so here is a solution that returns a const_iterator
(others
is const):
#include <unordered_set>
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo
{
public:
template <class Hash>
typename std::unordered_set<Foo, Hash>::const_iterator findClosest(std::unordered_set<Foo, Hash> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
int main()
{
Foo f;
std::unordered_set<Foo> fs;
f.findClosest(fs);
}
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
@Azias It actually makes no difference because bothiterator
andconst_iterator
are const iterators (and may be the same type) forstd::set
andstd::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".
– Max Langhof
Nov 16 '18 at 9:58
add a comment |
I see that you tagged the Question as C++11 (so we cannot rely on type deduction for the return type), so here is a solution that returns a const_iterator
(others
is const):
#include <unordered_set>
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo
{
public:
template <class Hash>
typename std::unordered_set<Foo, Hash>::const_iterator findClosest(std::unordered_set<Foo, Hash> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
int main()
{
Foo f;
std::unordered_set<Foo> fs;
f.findClosest(fs);
}
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
@Azias It actually makes no difference because bothiterator
andconst_iterator
are const iterators (and may be the same type) forstd::set
andstd::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".
– Max Langhof
Nov 16 '18 at 9:58
add a comment |
I see that you tagged the Question as C++11 (so we cannot rely on type deduction for the return type), so here is a solution that returns a const_iterator
(others
is const):
#include <unordered_set>
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo
{
public:
template <class Hash>
typename std::unordered_set<Foo, Hash>::const_iterator findClosest(std::unordered_set<Foo, Hash> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
int main()
{
Foo f;
std::unordered_set<Foo> fs;
f.findClosest(fs);
}
I see that you tagged the Question as C++11 (so we cannot rely on type deduction for the return type), so here is a solution that returns a const_iterator
(others
is const):
#include <unordered_set>
class Foo;
namespace std
{
template <>
struct hash<Foo>
{
size_t operator()(Foo const & bar) const;
};
}
class Foo
{
public:
template <class Hash>
typename std::unordered_set<Foo, Hash>::const_iterator findClosest(std::unordered_set<Foo, Hash> const &others)
{
return std::end(others);
}
size_t hashValue() const {
return std::hash<int>()(m_Member);
}
private:
int m_Member;
};
size_t std::hash<Foo>::operator()(Foo const & bar) const
{
return bar.hashValue();
}
int main()
{
Foo f;
std::unordered_set<Foo> fs;
f.findClosest(fs);
}
answered Nov 16 '18 at 9:48
JonasJonas
5,79982442
5,79982442
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
@Azias It actually makes no difference because bothiterator
andconst_iterator
are const iterators (and may be the same type) forstd::set
andstd::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".
– Max Langhof
Nov 16 '18 at 9:58
add a comment |
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
@Azias It actually makes no difference because bothiterator
andconst_iterator
are const iterators (and may be the same type) forstd::set
andstd::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".
– Max Langhof
Nov 16 '18 at 9:58
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
you are right, I must return const_iterator
– Azias
Nov 16 '18 at 9:53
@Azias It actually makes no difference because both
iterator
and const_iterator
are const iterators (and may be the same type) for std::set
and std::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".– Max Langhof
Nov 16 '18 at 9:58
@Azias It actually makes no difference because both
iterator
and const_iterator
are const iterators (and may be the same type) for std::set
and std::unordered_set
: "Container elements may not be modified (even by non const iterators) since modification could change an element's hash and corrupt the container.".– Max Langhof
Nov 16 '18 at 9:58
add a comment |
You might find the code is clearer and easier to maintain if you move the concerns of hashing Foo and finding closest distance into free functions.
Here's an example using boost::hash
protocols (which I find to be very helpful)
#include <unordered_set>
#include <boost/functional/hash.hpp>
class Foo
{
public:
// return a tuple of immutable values which should be hashed in order to
// compute this object's hash value
auto hashy_stuff() const // -> std::tuple<const int&> // if you are < c++14
{
return std::tie(m_Member);
}
private:
int m_Member;
};
// implement the boost::hash hash_value protocol in terms of hashy_stuff
// (we win because now it's DRY and works with all boost hash functions)
auto hash_value(Foo const& foo) -> std::size_t { return boost::hash_value(foo.hashy_stuff()); }
// implement std::hash in terms of boost::hash (again, DRY because it automatically
// uses hash_value())
namespace std { template<> struct hash<Foo> : ::boost::hash<Foo> {}; }
// implement findClosest as a free function. Its concerns are tangential to
// the Foo-ness of a Foo.
std::unordered_set<Foo>::const_iterator
findClosest(Foo const& /*foo*/, std::unordered_set<Foo> const &others)
{
// implementation here in terms of public interface
return std::end(others);
}
extern auto make_foos() -> std::unordered_set<Foo>;
extern auto make_foo() -> Foo;
bool test()
{
auto foos = make_foos();
auto foo = make_foo();
auto i = findClosest(foo, foos);
return i != end(foos);
}
add a comment |
You might find the code is clearer and easier to maintain if you move the concerns of hashing Foo and finding closest distance into free functions.
Here's an example using boost::hash
protocols (which I find to be very helpful)
#include <unordered_set>
#include <boost/functional/hash.hpp>
class Foo
{
public:
// return a tuple of immutable values which should be hashed in order to
// compute this object's hash value
auto hashy_stuff() const // -> std::tuple<const int&> // if you are < c++14
{
return std::tie(m_Member);
}
private:
int m_Member;
};
// implement the boost::hash hash_value protocol in terms of hashy_stuff
// (we win because now it's DRY and works with all boost hash functions)
auto hash_value(Foo const& foo) -> std::size_t { return boost::hash_value(foo.hashy_stuff()); }
// implement std::hash in terms of boost::hash (again, DRY because it automatically
// uses hash_value())
namespace std { template<> struct hash<Foo> : ::boost::hash<Foo> {}; }
// implement findClosest as a free function. Its concerns are tangential to
// the Foo-ness of a Foo.
std::unordered_set<Foo>::const_iterator
findClosest(Foo const& /*foo*/, std::unordered_set<Foo> const &others)
{
// implementation here in terms of public interface
return std::end(others);
}
extern auto make_foos() -> std::unordered_set<Foo>;
extern auto make_foo() -> Foo;
bool test()
{
auto foos = make_foos();
auto foo = make_foo();
auto i = findClosest(foo, foos);
return i != end(foos);
}
add a comment |
You might find the code is clearer and easier to maintain if you move the concerns of hashing Foo and finding closest distance into free functions.
Here's an example using boost::hash
protocols (which I find to be very helpful)
#include <unordered_set>
#include <boost/functional/hash.hpp>
class Foo
{
public:
// return a tuple of immutable values which should be hashed in order to
// compute this object's hash value
auto hashy_stuff() const // -> std::tuple<const int&> // if you are < c++14
{
return std::tie(m_Member);
}
private:
int m_Member;
};
// implement the boost::hash hash_value protocol in terms of hashy_stuff
// (we win because now it's DRY and works with all boost hash functions)
auto hash_value(Foo const& foo) -> std::size_t { return boost::hash_value(foo.hashy_stuff()); }
// implement std::hash in terms of boost::hash (again, DRY because it automatically
// uses hash_value())
namespace std { template<> struct hash<Foo> : ::boost::hash<Foo> {}; }
// implement findClosest as a free function. Its concerns are tangential to
// the Foo-ness of a Foo.
std::unordered_set<Foo>::const_iterator
findClosest(Foo const& /*foo*/, std::unordered_set<Foo> const &others)
{
// implementation here in terms of public interface
return std::end(others);
}
extern auto make_foos() -> std::unordered_set<Foo>;
extern auto make_foo() -> Foo;
bool test()
{
auto foos = make_foos();
auto foo = make_foo();
auto i = findClosest(foo, foos);
return i != end(foos);
}
You might find the code is clearer and easier to maintain if you move the concerns of hashing Foo and finding closest distance into free functions.
Here's an example using boost::hash
protocols (which I find to be very helpful)
#include <unordered_set>
#include <boost/functional/hash.hpp>
class Foo
{
public:
// return a tuple of immutable values which should be hashed in order to
// compute this object's hash value
auto hashy_stuff() const // -> std::tuple<const int&> // if you are < c++14
{
return std::tie(m_Member);
}
private:
int m_Member;
};
// implement the boost::hash hash_value protocol in terms of hashy_stuff
// (we win because now it's DRY and works with all boost hash functions)
auto hash_value(Foo const& foo) -> std::size_t { return boost::hash_value(foo.hashy_stuff()); }
// implement std::hash in terms of boost::hash (again, DRY because it automatically
// uses hash_value())
namespace std { template<> struct hash<Foo> : ::boost::hash<Foo> {}; }
// implement findClosest as a free function. Its concerns are tangential to
// the Foo-ness of a Foo.
std::unordered_set<Foo>::const_iterator
findClosest(Foo const& /*foo*/, std::unordered_set<Foo> const &others)
{
// implementation here in terms of public interface
return std::end(others);
}
extern auto make_foos() -> std::unordered_set<Foo>;
extern auto make_foo() -> Foo;
bool test()
{
auto foos = make_foos();
auto foo = make_foo();
auto i = findClosest(foo, foos);
return i != end(foos);
}
answered Nov 16 '18 at 9:51
Richard HodgesRichard Hodges
55.4k657100
55.4k657100
add a comment |
add a comment |
Instead of specializing template in std
namespace you can try below method if you like that.
#include <iostream>
#include <unordered_set>
struct FooHash;
class Foo {
public:
Foo(){
}
~Foo(){
}
friend FooHash;
private:
std::string m_str;
};
struct FooHash
{
std::size_t operator()(Foo const& f) const noexcept{
return std::hash<std::string>{}(f.m_str);
}
};
int main(){
std::unordered_set<Foo,FooHash> fSet;
return 0;
}
Live Code
This completely ignores the core of the question: Usingstd::unordered_set<Foo>
in the interface ofFoo
.
– Max Langhof
Nov 16 '18 at 8:58
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
add a comment |
Instead of specializing template in std
namespace you can try below method if you like that.
#include <iostream>
#include <unordered_set>
struct FooHash;
class Foo {
public:
Foo(){
}
~Foo(){
}
friend FooHash;
private:
std::string m_str;
};
struct FooHash
{
std::size_t operator()(Foo const& f) const noexcept{
return std::hash<std::string>{}(f.m_str);
}
};
int main(){
std::unordered_set<Foo,FooHash> fSet;
return 0;
}
Live Code
This completely ignores the core of the question: Usingstd::unordered_set<Foo>
in the interface ofFoo
.
– Max Langhof
Nov 16 '18 at 8:58
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
add a comment |
Instead of specializing template in std
namespace you can try below method if you like that.
#include <iostream>
#include <unordered_set>
struct FooHash;
class Foo {
public:
Foo(){
}
~Foo(){
}
friend FooHash;
private:
std::string m_str;
};
struct FooHash
{
std::size_t operator()(Foo const& f) const noexcept{
return std::hash<std::string>{}(f.m_str);
}
};
int main(){
std::unordered_set<Foo,FooHash> fSet;
return 0;
}
Live Code
Instead of specializing template in std
namespace you can try below method if you like that.
#include <iostream>
#include <unordered_set>
struct FooHash;
class Foo {
public:
Foo(){
}
~Foo(){
}
friend FooHash;
private:
std::string m_str;
};
struct FooHash
{
std::size_t operator()(Foo const& f) const noexcept{
return std::hash<std::string>{}(f.m_str);
}
};
int main(){
std::unordered_set<Foo,FooHash> fSet;
return 0;
}
Live Code
answered Nov 16 '18 at 8:54
Manthan TilvaManthan Tilva
1,296823
1,296823
This completely ignores the core of the question: Usingstd::unordered_set<Foo>
in the interface ofFoo
.
– Max Langhof
Nov 16 '18 at 8:58
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
add a comment |
This completely ignores the core of the question: Usingstd::unordered_set<Foo>
in the interface ofFoo
.
– Max Langhof
Nov 16 '18 at 8:58
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
This completely ignores the core of the question: Using
std::unordered_set<Foo>
in the interface of Foo
.– Max Langhof
Nov 16 '18 at 8:58
This completely ignores the core of the question: Using
std::unordered_set<Foo>
in the interface of Foo
.– Max Langhof
Nov 16 '18 at 8:58
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
@MaxLanghof Sorry My mistake
– Manthan Tilva
Nov 16 '18 at 9:15
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53334015%2fstdhash-specialisation-for-my-own-class-and-use-it-inside-the-class%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
MSVC in VS 2015 does not implement two-phase lookup correctly, so certain templated things working (or not) when they don't (or do) in gcc/clang is basically coincidental. If you're writing new code, you should not rely on this, as it will eventually break.
– Max Langhof
Nov 16 '18 at 10:07
Yes, I just tried in my real case, and it does no work. So I will fallback to the pointer version
– Azias
Nov 16 '18 at 10:19
Actually I am stuck because I cannot use to Pointer version neither, because it is not possible to get a
Foo*
from astd::unordered_set<Foo>::iterator
– Azias
Nov 16 '18 at 10:30
What's wrong with
&*myIt
(aside from syntactic awkwardness)?– Max Langhof
Nov 16 '18 at 10:32
my bad, actually I have to return
Foo const *
and not aFoo *
, that's why I had problem with the Pointer version.– Azias
Nov 16 '18 at 10:40