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

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";

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() {
// ...
}

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

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.

How to initialize static const char* enum type traits array?

I've encountered a header in an external sdk like so:
// external.h
//
template <class T> class MyBaseEnum
{
public:
/** String list. */
static const char *mStrings[];
//! Constructor.
inline MyBaseEnum(){}
//! Destructor.
inline ~MyBaseEnum()
{
}
};
I've seen this class used in the same sdk in another header like so:
// foo.h
//
class Foo
{
enum MyEnum
{
A = 0,
B,
C
};
typedef MyBaseEnum< MyEnum > MyEnumType;
MyEnumType bar;
};
I don't have access to the corresponding cpp file to see how mStrings is initialized for bar, but I think this is related to type traits.
What would the syntax look like in foo.cpp to correctly initialize MyEnumType::mStrings?
As MyBaseEnum is a templated class, the mStrings member can't be defined in a separate source file. It has to be done in a header file that is included, as the definition requires the template argument.
The syntax is basically the same as defining any other string array:
template<class T>
const char* MyBaseEnum<T>::mStrings = { ... };

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.

Initialize an static member in an fully specialized class template

I can't seem to init an static member inside an fully specialized class template!
I'm trying to do the following:
template<typename Type>
class X
{
};
template<>
class X<int>
{
public:
static int Value;
}
But i can't seem to init the static member, i tried everything like:
template<>
int X<int>::Value = 0;
It doesn't compile, so any pointers on how to actually do this would be nice ;)
Edit: the answer beneath is correct but you also need to place the init in the .cpp file and not in the header file.
Thanks for your time,
Richard.
Don't use template<> while defining Value because template<> is not allowed in member definition of explicitly specialized class[X<int> in this case]. Moreover you are missing a semicolon after }
This works for me:
template<typename Type>
class X
{
};
template<>
class X<int>
{
public:
static int Value;
};
int X<int>::Value = 0;