How should I initialize a static variable for a partial specialization?
template <bool A=true, bool B=false>
struct from {
const static std::string value;
};
// no specialization - works
template <bool A, bool B>
const std::string from<A, B>::value = "";
// partial specialization - does not compile -
// Error: template argument list following class template name must list parameters in the order used in template parameter list
// Error: from<A,B>' : too few template arguments
template <bool B>
const std::string from<true, B>::value = "";
// full specialization - works
const std::string from<false, true>::value = "";
Why doesn't the partial work?
EDIT: I found a solution based on Partial template specialization for initialization of static data members of template classes
I need to repeat the declaration for the partial specialization before it allowed me to initialize the static variable:
template <bool B>
struct from<true, B> {
const static std::string value;
};
Again, the question is why?
Partial specialization of members (whether they're functions or static data) are not allowed without partial specialization of enclosing class template itself.
That is, you have to specialize the class template also. So the following should work:
//partial specialization of class template
template <bool B>
struct from<true, B> {
const static std::string value;
};
//now you can do this!
template <bool B>
const std::string from<true, B>::value = ""
Also, this will not compile (have you tried compiling this?):
// full specialization - works (SORRY, IT WILL NOT WORK!)
const std::string from<false, true>::value = ""; //this should be an error
You've to write this:
// full specialization
template<> //<---- this is important!
const std::string from<false, true>::value = ""
Here's a working full specialization of the template.
#include <string>
#include <iostream>
template <bool A=true, bool B=false>
struct from {
const static std::string value;
};
// no specialization - works
template <bool A, bool B>
const std::string from<A, B>::value = "no specialization";
// full specialization, note the empty template parameter list
template <>
const std::string from<true, true>::value = "<true,true> specialization";
int main() {
std::cout << from<false, false>::value << std::endl;
std::cout << from<true, true>::value << std::endl;
}
You found the correct way of defining the partial.
The reason for your partial not working is that you need to declare the structure type before being able to provide an initialization for its static field. The partial specialization is a template in its own right, and deserves a definition.
The full specialization is actually a type instance of the initial template, and thus doesn't need to be defined separately.
Related
I have a wrapper class for std::string that serves as base class for several others. Instances of the subclasses will be used as keys in std::unordered_set so I need to provide a hash function for them. Since the hash is only dependent on the std::string stored in the base class, I do not want to write a hash function for every subclass but rather use the one from the wrapper class.
This is how I would like to solve the problem:
#include <string>
#include <unordered_set>
class Wrapper {
public:
std::string name;
size_t _hash;
explicit Wrapper(std::string str) : name(str), _hash(std::hash<std::string>()(name)) {}
size_t hash() const { return _hash; }
};
class Derived : public Wrapper {};
namespace std {
template <> struct hash<Wrapper> {
std::size_t operator()(const Wrapper &k) const { return k.hash(); }
};
template <typename T> struct hash<std::enable_if_t<std::is_base_of_v<Wrapper, T>>> {
std::size_t operator()(const T &k) const { return k.hash(); }
};
} // namespace std
int main(void) {
std::unordered_set<Wrapper> m1;
std::unordered_set<Derived> m2;
}
This does not compile of course, since T cannot be deduced. Clang says:
20:30: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
20:20: note: non-deducible template parameter 'T'
And g++ says:
hash_subclass.cpp:21:30: error: template parameters not deducible in partial specialization:
template <typename T> struct hash<std::enable_if_t<std::is_base_of_v<Wrapper, T>>> {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
hash_subclass.cpp:21:30: note: 'T'
I have found this solution, but I would like to avoid using a macro. Also, this goes against what I expect from inheritance.
Is there a solution for this? Can a subclass inherit its base class' specialization of std::hash?
Also, I'm not 100% sure about my use of std::enable_if and std::is_base_of. Could you tell me whether this would work assuming T could be deduced?
IRC, the problem with std::enable_if is that it does not work for classes with a single template parameter. Consequently, you cannot specialize std::hash by using std::enable_if.
However, you can make your own hasher as follows:
template <typename T, typename Enable = std::enable_if_t<std::is_base_of_v<Wrapper, T>>>
struct WrapperHasher {
std::size_t operator()(const T& k) const { return k.hash(); }
};
And then use it as a second template argument of std::unordered_set:
std::unordered_set<Wrapper, WrapperHasher<Wrapper>> m1;
std::unordered_set<Derived, WrapperHasher<Derived>> m2;
But in your case, you can define a wrapper much more simply as:
struct WrapperHasher {
std::size_t operator()(const Wrapper& k) const { return k.hash(); }
};
And then write:
std::unordered_set<Wrapper, WrapperHasher> m1;
std::unordered_set<Derived, WrapperHasher> m2;
How is below code working to detect if the two classes are same? Specially I did not get the syntax second IsSame class. It does not look like the explicit initialization. Please let me know which link I should go through to understand this? I am following book 'C++ the complete reference' by Robbert Schildit.
template<typename A, typename B> struct IsSame {
static const bool same = false;
};
template<typename A> struct IsSame<A, A> {//have confusion here
static const bool same = true;
};
template<typename A, typename B> bool IsSameClass() {
return IsSame<A, B>::same;
}
int main(void)
{
cout << IsSameClass<vector<int>, vector<int>>();//returns 1
cout << IsSameClass<vector<int>, vector<float>>();//returns 0
}
The first definition of IsSame is a primary template. The second one is its partial specialization.
When you use IsSame within IsSameClass, if template arguments match the partial specialization (i.e., if both arguments represent the same type) then this partial specialization is instantiated and the function returns its same value which is true. Otherwise, the primary template is instantiated and function returns false.
I am trying to compile the below code, and I am getting the error as
"error: ‘MD5Sum’ is not a class template
template<> struct MD5Sum<::cv_bridge::CvImage>"
template<> struct MD5Sum<::cv_bridge::CvImage>
{
static const char* value() { return MD5Sum<::sensor_msgs::msg::Image>::value(); }
static const char* value(const ::cv_bridge::CvImage&) { return value(); }
static const uint64_t static_value1 = MD5Sum<::sensor_msgs::msg::Image>::static_value1;
static const uint64_t static_value2 = MD5Sum<::sensor_msgs::msg::Image>::static_value2;
// If the definition of sensor_msgs/Image changes, we'll get a compile error here.
ROS_STATIC_ASSERT(MD5Sum<::sensor_msgs::msg::Image>::static_value1 == 0x060021388200f6f0ULL);
ROS_STATIC_ASSERT(MD5Sum<::sensor_msgs::msg::Image>::static_value2 == 0xf447d0fcd9c64743ULL);
};
This seems to be template specialization. Does it means that template class should be present first before make template specialization out of it.
The class template MD5Sum needs to be declared before any of its specializations.
You either need to include the file where the template is declared, or declare it yourself.
You can use an empty definition (if you only want to call fully specialized versions) or the generic implementation you choose:
// add this before your specialization
template <class T> struct MD5Sum; // empty declaration
// or your default implementation
template <class T> struct MD5Sum {
...
};
When I try to compile this with Clang
template<class T>
struct Field
{
char const *name;
Field(char const *name) : name(name) { }
};
template<class Derived>
class CRTP { static Field<Derived> const _field; };
class Class : public CRTP<Class> { };
Field<Class> const CRTP<Class>::_field("blah");
int main() { }
I get
error: template specialization requires 'template<>'
Field<Class> const CRTP<Class>::_field("blah");
~~~~~~~~~~~ ^
I don't understand the error at all. What is wrong with my definition of _field and how do I fix it?
(Note that the arguments to _field are not necessarily the same for all subclasses.)
For the compiler to identify this as a template specialization (e.g. to be able to check the syntax), you need the template keyword:
template<>
Field<Class> const CRTP<Class>::_field("blah");
Its brackets are empty as all template parameters are specialized, but you cannot just leave it away.
The error says exactly what is missing. template<> is missing before that line.
template<>
Field<Class> const CRTP<Class>::_field("blah");
Note, however, that your typing of Field<Class>, if unique, could be used to construct all instances of Field<Class> with a given string.
template<typename T>
struct field_trait;
template<class T>
struct Field
{
char const *name;
Field() : name(field_trait<T>::get_name()) {}
};
template<class Derived>
class CRTP { static Field<Derived> const _field; };
template<class Derived>
class CRTP<Derived>::_field;
class Class;
template<>
struct field_traits<Class> {
static const char* get_name() { return "blah"; }
};
class Class : public CRTP<Class> { };
int main() { }
which means that every instance of Field<Class> always has the name "blah".
One question I would have is, do you really need storage for said Field<Class> to actually have a pointer to a string, and if so does it need to be unique, and if so does it need to be "bare"? Because figuring out where the static instance exists is somewhat annoying.
Together with field_traits above:
template<class Derived>
class CRTP { static Field<Derived>& field() const { static Field<Derived> _field( field_traits<Derived>::get_name()); return _field; };
this moves the problem of "where is the _field stored" to being the compilers problem. And it is initialized by the contents of field_traits<T>::get_name().
A static data member must have both a declaration and a definition. If this was a plain class it would look like this:
// header:
class C {
static int i;
};
// source:
int C::i = 3;
Templates aren't ordinarily defined in source files, so the code would look something like this:
// header:
template <class T>
class C {
static int i;
};
template <class T>
int C<T>::i = 3;
In your code, you don't have the definition of the static data member. That's okay if you don't use it. But the code that the compiler is complaining about defines a static data member for CRTP<Class>; that's a specialization (because it's not applicable to all instantiations of CRTP, just to this one), and the compiler is saying that you have to tell it that it's a specialization. So do as you're told:
template <>
Field<Class> const CRTP<Class>::_field("blah");
or, to write the non-specialized template version, use the usual template syntax:
template <class T>
Field<T> const CRTP<T>::_field("blah");
I'm trying to have a different template specialization for classes which have an inner class with a particular name present. I've taken a clue from here and tried the following:
#include <iostream>
template< typename T, typename Check = void > struct HasXYZ
{ static const bool value = false; };
template< typename T > struct HasXYZ< T, typename T::XYZ >
{ static const bool value = true; };
struct Foo
{
class XYZ {};
};
struct FooWithTypedef
{
typedef void XYZ;
};
int main()
{
// The following line prints 1, as expected
std::cout << HasXYZ< FooWithTypedef >::value << std::endl;
// The following line prints 0. Why?
std::cout << HasXYZ< Foo >::value << std::endl;
return 0;
}
As you can see, if I test for a typedef-defined type in FooWithTypedef, it works. However, it does not work if the type is a genuine inner class. It also only works when the typedef-ed type in FooWithTypedef matches the default value of the second argument in the initial template declaration (which is void in my example). Could one explain what is going on here? How does the specialization process work here?
Answer to the initial question
The template specialization you defined here:
template <typename T> struct HasXYZ <T,typename T::XYZ>
{ static const bool value = true; };
will take effect when somebody uses the data type HasXYZ<A,A::XYZ> for some data type A.
Note that, whatever A is, A::XYZ is a data type totally independent of A. Inner classes are data types in their own right. When you use A as the first template argument, there is absolutely no reason for the compiler to assume that you want to use something called A:XYZ as the second argument, even if an inner class of that name exists, and even if doing so would lead the compiler to a template specialization that matches the template arguments exactly. Template specializations are found based on the template arguments provided by the coder, not based on further possible template arguments.
Hence when you use HasXYZ<Foo>, it falls back to using the default template argument void for the second parameter.
Needless to say that if you were to use HasXYZ<Foo,Foo:XYZ> explicitly, you'd get the expected output. But that obviously is not what you intended.
I am afraid the only way to get what you need is std::enable_if (or something that works in a similar way).
Answer to the additional question (after update)
Consider the simplification below:
template <typename T, typename Check = void>
struct A
{ static const bool value = false; };
template <typename T>
struct A<T,void>
{ static const bool value = true; };
The primary definition specifies a default argument of void for the second template parameter. But the specialization (second definition above) defines what class A actually looks like if the second template parameter really is void.
What this means is that if you use, say, A<int> in your code, the default argument will be supplemented so you get A<int,void>, and then the compiler finds the most fitting template specialization, which is the second one above.
So, while default template arguments are defined as part of the primary template declaration, making use of them does not imply that the primary template definition is used. This is basically because default template arguments are part of the template declaration, not the template definition (*).
This why in your example, when typedef void XYZ is included in FooWithTypedef, the second template parameter defaults to void and then the most fitting specialization is found. This works even if in the template specialization the second argument is defined as T::XYZ instead of void. If these are the same at the time of evaluation, the template specialization will be selected (§14.4 "Type equivalence").
(*) I didn't find a statement in the Standard that actually says it so clearly. But there is §14.1/10, which describes the case where you have multiple declarations (but only one primary definition) of a template:
(§14.1/10) The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (8.3.6). [ Example:
template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;
is equivalent to
template<class T1 = int, class T2 = int> class A;
].
This suggests that the mechanism behind default template arguments is independent of that used to identify the most fitting specialization of the template.
In addition, there are two existing SO posts that refer to this mechanism as well:
This reply to Template specialization to use default type if class member typedef does not exist
Default values of template parameters in the class template specializations
Here is another version that detects the presence of the inner class :
#include <iostream>
template< typename T >
struct HasXYZ
{
typedef char yes;
typedef struct{ char d[2]; } no;
template<typename T1>
static yes test( typename T1::XYZ * );
template<typename T1>
static no test(...);
static const bool value = ( sizeof( test<T>(0) ) == sizeof( yes ) );
};
struct Foo
{
class XYZ {};
};
struct Bar
{
class ABC {};
};
int main()
{
std::cout << std::boolalpha << HasXYZ< Foo >::value << std::endl;
std::cout << std::boolalpha << HasXYZ< Bar >::value << std::endl;
}
A::XYZ would need to void to have the partial specialization selected, which can never be the case for a class type. One way to make it work is using a fake dependant void typename:
template<class T>
struct void_{ typedef void type; };
template<class T, class = void>
struct has_XYZ{ static bool const value = false; };
template<class T>
struct has_XYZ<T, typename void_<typename T::XYZ>::type>{
static bool const value = true;
};
For an explanation on how this works, see this question.