Hash function for user defined class. How to make friends? :) - c++

I have a class C, which has a string* ps private data member.
Now, I'd like to have an unordered_map<C, int> for which I need a custom hash function.
According to the c++ reference, I can do that like
namespace std {
template<>
class hash<C> {
public:
size_t operator()(const C &c) const
{
return std::hash<std::string>()(*c.ps);
}
};
}
The problem is that I can't seem to make operator() and C friends so that I could access ps.
I have tried this:
class C;
template<>
class std::hash<C>;
class C{
//...
friend std::hash<C>::operator ()(const C&) const; // error: Incomplete type
};
// define hash<C> here.
but it says that Incomplete type ... in nested name specifier ...
I can't turn around the definitions either, because if class C is defined later, the hash<C> has no way to know about ps.
What am I doing wrong here? How can this situation be fixed without making ps public?

Try this:
class C;
namespace std {
template<>
struct hash<C> {
public:
size_t operator()(const C &c) const; // don't define yet
};
}
class C{
//...
friend size_t std::hash<C>::operator ()(const C&) const;
};
namespace std {
template<>
size_t hash<C>::operator()(const C &c) const {
return std::hash<std::string>()(*c.ps);
}
}
Or this:
class C;
template<>
struct std::hash<C>;
class C{
friend struct std::hash<C>; // friend the class, not the member function
};
(I haven't compiled so there might be a syntax error)

Simpler way to make friends:
class C {
template <typename T> friend class std::hash;
};
This works even when C is in its own namespace or is a nested class, as std::hash won't have to be forward declared.

I'd suggest to add method like following
class C
{
....
public: const string* get_ps() const { return ps; }
....
};
and use it in the your hash specialization.

Related

How to make a function that uses incomplete type objects

I want to make a class one that can build up class two by func1.
I have two type of class overloading. The primary template class, and the overloading template specialization class.
Primary class one is fine. But class one<void> can not implement func1.
The compiler complains return type 'class two<void>' is incomplete, aggregate 'two<void> t' has incomplete type and cannot be defined, and two<void> used in a nested specifier, stdx::shared_future<void>::num_copies_ = 2;.
What can I do to make a one version of func1?
Thank you.
template<class T2>
class two;
template<>
class two<void>;
template<class T1>
class one
{
// ...
public:
two<T1> func1()
{
two<T1> t;
two<T1>::two__ = 1;
//...
// manipulation of obj t.
//...
return t;
}
// ...
};
template<>
class one<void>
{
// ...
public:
two<void> func1()
{
two<void> t;
two<void>::two__ = 1;
//...
// manipulation of obj t.
//...
return t;
}
// ...
};
template<class T2>
class two
{
static int two__;
// definition of two
};
template<>
class two<void>
{
public:
static int two__;
// definition of two<void>
};
To avoid errors related to incomplete types the one::func1 method can be defined after two. It can be declared like this:
template<>
class one<void> {
public:
two<void> func1();
};
And then, after two has been defined:
two<void> one<void>::func1() {
// ...
}

A trouble in definition a friend function of template class in the namespace in c++

namespace N
{
template<typename tname> class C
{
public:
friend void f(int, C<tname>);
};
}
template<typename tname>
void N::f(int, N::C<tname>)
{
}
I am creating a friend function f like above but the compiler informed me this error :
'f': is not a member of N.
Meanwhile, without the namespace, my code will run correcly.
My code will be correct too when I use normal class instead of generic class (no remove namespace).
And it will also have no error with fully declaration. Like following code:
namespace N
{
template<typename tname> class C
{
public:
friend void f(int, C<tname>)
{
//mycode
}
};
}
Could someone help me fix this error in my first code? Thank you very much.
The friend declaration does not mean that the function is now a member of that class. It only means that f has access to the private members of C.
So if f is in the global namespace it cannot be referenced as being inside namespace N with N::f, it is still only a ::f, even after the friend declaration.
In the second example you're declaring f as a member function and declaring it a friend which I'm not sure serves a purpose.
The following compiles:
namespace N
{
template<typename tname>
class C;
template<typename tname>
void f(int, C<tname>);
template<typename tname> class C
{
public:
friend void f(int, C<tname>);
};
}
template<typename tname>
void N::f(int, N::C<tname>)
{
}
int main()
{
}

How do you initialize a static member of an inaccessible template class?

I have code similar to the following, where a class template has a static variable that needs to be initialized. I then have a template class typedef that uses a private, inner class as the template argument:
template <typename T>
class Foo
{
private:
static const char* s_name;
};
class Bar
{
private:
class Baz
{
// ...
};
typedef Foo<Baz> FooBaz;
};
I thought I could initialize the static variable like this:
template<>
const char* Foo<Bar::Baz>::s_name = "foobaz";
And it works... in MS Visual Studio 2015. However, when I build with clang, I get the an error like the following:
Error 'Baz' is a protected member of 'Bar'
Why does this work with MSVS, but not clang? Is there a way to initialize this variable that will work with both?
Make a "typedef Foo FooBaz" public in "Bar" and use "Bar::FooBaz::s_name".
You can always use that, if you do not care about partial instantiation.
template<typename T>
const char * Foo<T>::s_name = "foo";
for partials you need to make Foo a friend to Bar.
class Bar
{
private:
class Baz {
// ...
};
friend class Foo<Baz>;
typedef Foo<Baz> FooBaz;
};
template<>
const char * Foo<Bar::Baz>::s_name = "barbaz";

How can I access a protected constructor from a friend function?

I created a class and I want to force anyone who's trying to construct an object, to use unique_ptr. To do that I thought of declaring the constructor protected and use a friend function that returns a unique_ptr. So here's an example of what I want to do:
template <typename T>
class A
{
public:
friend std::unique_ptr<A<T>> CreateA<T>(int myarg);
protected:
A(int myarg) {}
};
template <typename T>
std::unique_ptr<A<T>> CreateA(int myarg)
{
// Since I declared CreateA as a friend I thought I
// would be able to do that
return std::make_unique<A<T>>(myarg);
}
I did some reading on friend functions and I understood that a friend function provides access to private/protected members of an object of a class.
Is there anyway I can make my example work?
Even without friend functions, my goal is to make the CreateA the only way for someone to create an object.
EDIT
I change the code a bit. I didn't mention that my class takes one template parameter. That makes things more complex apparently.
You can do it this way :-
#include <iostream>
#include <memory>
using namespace std;
class A
{
int arg;
public:
friend unique_ptr<A> CreateA(int myarg);
void showarg() { cout<<arg; }
protected:
A(int myarg): arg(myarg) {}
};
unique_ptr<A> CreateA (int myarg)
{
return std::unique_ptr<A>(new A(myarg));
}
int main()
{
int x=5;
unique_ptr<A> u = CreateA(x);
u->showarg();
return 0;
}
Output :-
5
If you don't want to use friend function you can make the function static & call it like this :-
unique_ptr<A> u = A::CreateA(x);
EDIT :-
In reply to your edit I rewrote the program & it goes like this :-
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
class A
{
T arg;
public:
static std::unique_ptr<A> CreateA(T myarg)
{
return std::unique_ptr<A>( new A(myarg) );
}
void showarg()
{
cout<<arg;
}
protected:
A(T myarg): arg(myarg) {}
};
int main()
{
int x=5;
auto u = A<int>::CreateA(x);
u->showarg();
return 0;
}
Simple & easy !!! But remember you cannot instantiate the object of A. Good Luck !!!
The other answers suggest using a static template function, which I agree is the best solution, because it is simpler.
My answer explains why your friend approach didn't work and how to use the friend approach correctly.
There are two problems in your original code. One is that make_unique is not actually a friend of A, so the call make_unique<A<T>>(myarg); does not have access to A's protected constructor. To avoid this , you can use unique_ptr<A<T>>(new A(myarg)) instead. Theoretically it would be possible to declare make_unique a friend but I'm not even sure of the right syntax for that.
The other issue is the template friends problem. Inside a class template, friend <function-declaration> actually declares a non-template friend.
The C++ FAQ suggests two possible workarounds. One of them is to define the friend function inline. However, in that case the function can only be found by argument-dependent lookup. But since the function does not take A<T> (or A<T> &) as argument, it can never be found this way. So this option is not viable to your situation -- it's more suited to operator overloading.
So the only fix is to declare (and optionally define) the template function before the class definition:
#include <memory>
template<typename T>
class A;
template <typename T>
std::unique_ptr<A<T>> CreateA(int myarg)
{
return std::unique_ptr<A<T>>{new A<T>(myarg)};
}
template <typename T>
class A
{
friend std::unique_ptr<A<T>> CreateA <> (int myarg);
// refers to existing template ^^
protected:
A(int myarg) {}
};
int main()
{
auto x = CreateA<int>(5);
}
Note: It is possible to declare CreateA where I have defined it, and put the function definition later. However, the code I have posted works -- despite A not being defined when new A<T>(myarg) appears in the source -- because CreateA is not instantiated until it is called, at which point A will be defined.
Create a static function that instantiates the protected constructor.
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include <memory>
using namespace std;
template< typename T >
class A
{
public:
static void CreateA(int myarg, std::unique_ptr<A<T>>& objA, T t) {
std::unique_ptr<A<T>> objB(new A(myarg, t));
objA = std::move(objB);
}
protected:
A(int myarg, T t) {
m_t = t;
}
private:
T m_t;
};
int main() {
int myArg = 0;
std::unique_ptr<A<int>> anotherObjA;
A<int>::CreateA(myArg, anotherObjA, myArg);
return 0;
}

very non-trivial template friend declaration

class Foo
{
template <typename T> friend void T::persist(void);
int test;
};
class Bar
{
Foo foo;
void persist(void) { foo.test = 42; } // fails
}
With this, I hope that persist() member method of every class which defines it will be a friend of foo. The strange friend line compiles, but seems to do nothing.
Thank you in advance.
You cannot do that. You cannot befriend a member of all types, and your template-friend declaration is ill-formed. There is no template in:
class Foo
{
template <typename T>
friend void T::persist(void);
int test;
};
Note that the friend declaration is not a template. You can befriend a template function or class, but there is neither of them in the code above.
A simple workaround would be to create a helper class from which you derive and provides the accessor:
class Foo {
friend class FooAccessor;
int value;
};
class FooAccessor {
protected:
void write( Foo& f, int value ) {
f.value = value;
}
};
class FooUser : private FooAccessor {
Foo f;
void persist() {
write( f, 42 );
}
};
But you might want to revisit the design and look for alternatives, note that making fields private and then allowing every other class to access them through a friend declaration is not much better than just having the fields public. If you care to explain what you are trying to achieve, someone might be able to help you.