I would like to define a C++ template specialization that applies to all subclasses of a given base class. Is this possible?
In particular, I'd like to do this for STL's hash<>. hash<> is defined as an empty parametrized template, and a family of specializations for specific types:
template<class _Key>
struct hash { };
template<>
struct hash<char>
{
size_t
operator()(char __x) const
{ return __x; }
};
template<>
struct hash<int>
{
size_t
operator()(int __x) const
{ return __x; }
};
...
I would like to define something like this:
template<class Base>
struct hash {
size_t operator()(const Base& b) const {
return b.my_hash();
}
};
class Sub : public Base {
public:
size_t my_hash() const { ... }
};
and be able to use it like this:
hash_multiset<Sub> set_of_sub;
set_of_sub.insert(sub);
However, my hash template conflicts with the generic one from STL. Is there a way (perhaps using traits) to define a template specialization that applies to all subclasses of a given base class (without modifying the STL definitions)?
Note that I know I can do this with some extra template parameters whenever this hash specialization is needed, but I'd like to avoid this if possible:
template<>
struct hash<Base> {
size_t operator()(const Base& b) const {
return b.my_hash();
}
};
....
// similar specialization of equal_to is needed here... I'm glossing over that...
hash_multiset<Sub, hash<Base>, equal_to<Base> > set_of_sub;
set_of_sub.insert(sub);
Since C++ 11 you can use SFINAE together with standard library enable_if and is_base_of to solve the problem.
C++20 makes a cleaner solution possible - basically equivalent to enable_if, which even (optionally) works with CRTP
#include <concepts>
#include <functional>
#include <unordered_set> // just for demo in main()
template <class T>
class Base {};
class Derived final : public Base<Derived> {};
template<class T>
requires std::derived_from<T, Base<T>>
struct std::hash<T> {
// constexpr is optional
constexpr size_t operator() (const T& value) const noexcept {
return 0xDEADBEEF; // FIXME: do something better :)
}
};
int main() {
// If operator() weren't constexpr, this couldn't be a *static* assert
static_assert(std::hash<Derived>()(Derived {}) == 0xDEADBEEF);
std::unordered_set<Derived> usageTest;
return 0;
}
The solution is to use SFINAE to decide whether or not to allow your specialisation depending on the class inheritance structure. In Boost you can use enable_if and is_base_of to implement this.
http://www.boost.org/doc/libs/1_47_0/libs/utility/enable_if.html
http://www.boost.org/doc/libs/1_47_0/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html
This was the best I could do:
template<>
struct hash<Sub> : hash<Base> {
};
I'm a little worried that I didn't have to make operator() virtual, though.
I don't think it is possible, because the way to do template specialization based on something more complex than just matching the type is C++ SFINAE, which requires second (dummy) template argument. Unfortunatelly, std::hash takes only one template argument and it is not allowed to create another version of std::hash with two template arguments. Therefore, the if you aren't satisfied with Jayen's solution, you can create your own hash type:
#include <iostream>
#include <type_traits>
using namespace std;
class ParentClass {};
class ChildClass : public ParentClass {};
// SFINAE, if T is not a child of ParentClass, substitution will fail
// You can create specializations for the general case, for another base classes, etc.
template<typename T, typename=typename enable_if<is_base_of<ParentClass, T>::value, T>::type>
struct your_hash
{
size_t operator()(const T& value)
{
return 42;
}
};
int main()
{
ParentClass pc;
ChildClass cc;
cout<<your_hash<ParentClass>()(pc)<<"\n";
cout<<your_hash<ChildClass>()(cc)<<"\n";
}
Related
I write a template class dependent on a given type and variadic types, like so:
template<typename ConstType,typename...Inputs>
class ConstantTensor;
Then I write another class, which is generally defined in this way (assume wrong_type whatever type you want, but which is different from the following specialization ):
template<typename T>
class X{
public:
using type=wrong_type;
}
And I also have a specialization of this kind:
template<typename ConstType,typename...Inputs>
class X< ConstantTensor< ConstType ,Inputs...>>
{
public:
using type=right_type;
}
My problem is that, if I define the type ConstantTensor<ConstType,double> and then I want to use X<ConstantTensor<ConstType,double>>::type, the general case is called and not the specialization. So I obtain wrong_type instead of right_type. I guess it has to deal with the double type...Could you explain me why and how can I solve this issue? Thank you in advance.
EDIT:
Here a snippet of code, I hope it works:
class Scalar
{};
template<typename ConstType,typename...Inputs>
class ConstantTensor
{
public:
constexpr ConstantTensor(const Inputs&...inputs)
{}
};
template<typename ConstType,typename...Inputs>
constexpr auto Constant(const Inputs&...inputs)
{return ConstantTensor<ConstType,Inputs...>(inputs...);}
template<typename T>
class X{
public:
using type=int;
};
template<typename ConstType,typename...Inputs>
class X<ConstantTensor<ConstType,Inputs...>>{
public:
using type=char;
};
int main()
{
constexpr auto delta=Constant<Scalar>(2.0);
using type= X<decltype(delta)>::type; // this is int not char
}
The problem is that
constexpr auto delta=Constant<Scalar>(2.0);
is a constexpr variable; so it's also const.
So decltype(delta) isn't ConstantTensor<Scalar> but is a ConstantTensor<Scalar> const.
You can verify adding const in partial specialization declaration
template<typename ConstType,typename...Inputs>
class X<ConstantTensor<ConstType,Inputs...> const>{ // <-- added const
public:
using type=char;
};
Now you get that type is char.
-- EDIT --
The OP asks
Is there a short/elegant way to deal with both cases, const and non const, without duplicating the code?
I don't know if it's elegant, but it seems to me short enough: you can use a sort of self-inheritance adding the following partial specialization.
template <typename T>
class X<T const> : public X<T>
{ };
So X<ConstantTensor<Scalar> const> inherit from X<ConstantTensor<Scalar>>.
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;
I have a templated function defined as:
template<typename TObject> TObject Deserialize(long version, const Value &value)
what I need to do, is to write a specialization which would take vector defined as:
template<typename TNum, int cnt> class Vec
and still has access to cnt and TNum.
I have unsuccesfully tried
template<typename TNum, int cnt> Vec<TNum, cnt> Deserialize<Vec<TNum, cnt>>(long version, Value &value)
resulting in error: illegal use of explicit template arguments
What is the correct way to do it?
Usually, the correct answer to dealing with function templates and needing to partially specialize them, is to simply overload them instead. In this case this trick doesn't work directly because there are no arguments that depend on the template parameter, i.e. the template parameter is explicitly specified and not deduced. However, you can forward along to implementation functions, and make overloading work by using a simple tag struct.
#include <functional>
#include <iostream>
#include <type_traits>
#include <vector>
#include <array>
template <class T>
struct tag{};
template<typename TObject>
TObject Deserialize_impl(long version, tag<TObject>) {
std::cerr << "generic\n";
return {};
}
template<typename T, std::size_t N>
std::array<T,N> Deserialize_impl(long version, tag<std::array<T,N>>) {
std::cerr << "special\n";
return {};
}
template<typename TObject>
TObject Deserialize(long version) {
return Deserialize_impl(version, tag<TObject>{});
}
int main() {
Deserialize<int>(0);
Deserialize<std::array<int,3>>(0);
return 0;
}
Live example: http://coliru.stacked-crooked.com/a/9c4fa84d2686997a
I generally find these approaches strongly preferable to partial specialization of a struct with a static method (the other major approach here) as there are many things you can take advantage with functions, and it behaves more intuitively compared to specialization. YMMV.
While the functional tag-dispatch is a nice approach, here's a class specialization version for comparison. Both have their use, and I don't think either is an inherently regrettable decision but maybe one matches your personal style more.
For any class you write that needs a custom deserialize handler, just write a specialization of the Deserializer class:
#include <iostream>
#include <string>
using namespace std;
using Value = std::string;
// default deserialize function
template <typename TObject>
struct Deserializer {
static TObject deserialize(long version, const Value &value) {
std::cout << "default impl\n";
return TObject();
}
};
// free standing function (if you want it) to forward into the classes
template <typename TObject>
TObject deserialize(long version, const Value &value) {
return Deserializer<TObject>::deserialize(version, value);
}
// Stub example for your Vec class
template<typename TNum, int cnt> class Vec { };
// Stub example for your Vec deserializer specialization
template <typename TNum, int cnt> struct Deserializer<Vec<TNum, cnt>> {
static auto deserialize(long version, const Value &value) {
std::cout << "specialization impl: cnt=" << cnt << "\n";
return Vec<TNum, cnt>();
}
};
int main() {
Value value{"abcdefg"};
long version = 1;
deserialize<int>(version, value);
deserialize<Vec<int, 10>>(version, value);
}
Ideally in this situation, Vec should reflect its own template parameters as members Vec::value_type and Vec::size() which should be constexpr.
If the class fails to provide its own properties in its own interface, the next best thing is to define your own extension interface. In this situation, you can have separate metafunctions (like accessor functions), or a traits class (like a helper view class). I'd prefer the latter:
template< typename >
struct vector_traits;
template< typename TNum, int cnt >
struct vector_traits< Vec< TNum, cnt > > {
typedef TNum value_type;
constexpr static int size = cnt;
};
template<typename TVec> TVec Deserialize(long version, Value &value) {
typedef vector_traits< TVec > traits;
typedef typename traits::value_type TNum;
constexpr static int cnt = traits::size;
…
}
This solution fits into any existing function, and even makes the signatures cleaner. Also, the function is more flexible because you can adapt it by adding traits specializations instead of entire new overloads.
I have a templatized class like so :
template<typename T>
class A
{
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
}
I would like to add just ONE member function that would work only for 1 given type of T. Is it possible to do that at all without having to specialize the class and reimplement all the other already existing methods?
Thanks
The simplest and cleanest solution is to use a static_assert() in the body of a method, rejecting other types than the selected one (in the below example only integers are accepted):
#include <type_traits>
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
static_assert(std::is_same<T, int>::value, "Works only with ints!");
}
protected:
std::vector<T> myVector;
};
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
//f.onlyForInts(3.14f); // does not compile !
}
OK CASE DEMO
NOK CASE DEMO
This utilizes the fact that a compiler instantiates a member function of a class template only when one is actually used (not when the class template is instantiated itself). And with the above solution, when a compiler tries to do so, it fails due to the execution of a static_assert.
C++ Standard Reference:
§ 14.7.1 Implicit instantiation [temp.inst]
Unless a function template specialization has been explicitly instantiated or explicitly specialized, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist. Unless a call is to a function template explicit specialization or to a member function of an explicitly specialized class template, a default argument for a function template or a member function of a class template is implicitly instantiated when the function is called in a context that requires the value of the default argument.
[ Example:
template<class T> struct Z {
void f();
void g();
};
void h() {
Z<int> a; // instantiation of class Z<int> required
Z<char>* p; // instantiation of class Z<char> not required
Z<double>* q; // instantiation of class Z<double> not required
a.f(); // instantiation of Z<int>::f() required
p->g(); // instantiation of class Z<char> required, and
// instantiation of Z<char>::g() required
}
Nothing in this example requires class Z<double>, Z<int>::g(), or Z<char>::f() to be implicitly
instantiated. — end example ]
Yes, it's possible in C++03 with CRTP (Curiously recurring template pattern):
#include <numeric>
#include <vector>
template<typename Derived, typename T>
struct Base
{
};
template<typename Derived>
struct Base<Derived, int>
{
int Sum() const
{
return std::accumulate(static_cast<Derived const*>(this)->myVector.begin(), static_cast<Derived const*>(this)->myVector.end(), int());
}
};
template<typename T>
class A : public Base<A<T>, T>
{
friend class Base<A<T>, T>;
protected:
std::vector<T> myVector;
public:
/*
constructors + a bunch of member functions here
*/
};
int main()
{
A<int> Foo;
Foo.Sum();
}
As an alternative solution, which works also in plain C++03 (as opposed to static_assert or enable_if solutions), you may add extra defaulted template argument which will let you have both
specialized and unspecialized version of class. Then you can inherit your specialized version from the unspecialized one.
Here is a sample snippet:
#include <vector>
template<typename T, bool unspecialized = false>
class A
{
protected:
std::vector<T> myVector;
public:
void setVec(const std::vector<T>& vec) { myVector = vec; }
/*
constructors + a bunch of member functions here
*/
};
template<>
class A<int, false> : public A<int, true>
{
public:
int onlyForInt() {
return 25;
}
};
int main() {
// your code goes here
std::vector<int> vec;
A<int> a;
a.setVec(vec);
a.onlyForInt();
return 0;
}
The drawbacks of this solution is the need to add constructor forwarders, if class
has non-trivial constructors.
The static_assert technique by #PiotrS. works nicely. But it's also nice to know that you can specialize a single member function without code duplication. Just give the generic onlyForInts() an empty no-op implementation, and specialize it out-of-class for int
#include <vector>
template <typename T>
class A
{
public:
void onlyForInts(T t)
{
// no-op
}
protected:
std::vector<T> myVector;
};
template<>
void A<int>::onlyForInts(int t)
{
// works
}
int main()
{
A<int> i;
i.onlyForInts(1); // works !
A<float> f;
f.onlyForInts(3.14f); // compiles, but does nothing !
}
Live Example.
This technique comes in handy if you want to have int specific behavior without completely disabling the generic behavior.
One approach not given yet in the answers is using the standard library std::enable_if to perform SFINAE on a base class that you inherit to the main class that defines appropriate member functions.
Example code:
template<typename T, class Enable = void>
class A_base;
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>{
public:
void only_for_ints(){/* integer-based function */}
};
template<typename T>
class A_base<T, typename std::enable_if<!std::is_integral<T>::value>::type>{
public:
// maybe specialize for non-int
};
template<typename T>
class A: public A_base<T>{
protected:
std::vector<T> my_vector;
};
This approach would be better than an empty function because you are being more strict about your API and better than a static_cast because it simply won't make it to the inside of the function (it won't exist) and will give you a nice error message at compile time (GCC shows "has no member named ‘only_for_ints’" on my machine).
The downside to this method would be compile time and code bloat, but I don't think it's too hefty.
(don't you dare say that C++11 requirement is a down-side, we're in 2014 god-damnit and the next standard has even be finalized already!)
Also, I noticed, you will probably have to define my_vector in the base class instead of the final because you probably want to handle that data within the member function.
A nice way to do that without duplicating a bunch of code is to create a base base class (good god) and inherit that class in the base class.
Example:
template<typename T>
class base_data{
protected:
std::vector<T> my_vector;
};
template<typename T>
class A_base<T, typename std::enable_if<std::is_integral<T>::value>::type>: public base_bata<T>{
public:
void only_for_ints(){/* phew, finally. fiddle around with my_vector! */}
};
// non-integer A-base
template<typename T>
class A: public A_base<T>{
protected:
// helper functions not available in base
};
That does leave a horrible looking multiple-inheritance scheme, but it is very workable and makes it easy to define members based on template parameters (for future proofing).
People often don't like multiple-inheritance or how complicated/messy SFINAE looks, but I couldn't live without it now that I know of it: the speed of static code with the polymorphism of dynamic code!
Not sure where I found this, but you can use = delete; as the function definition inside the class, thereby deleting the function for the general case, and then explicitly specialize outside the class:
template <typename T>
struct A
{
auto int_only(T) -> void = delete;
};
template <> auto A<int>::int_only(int) -> void {}
int main()
{
auto a_int = A<int>{};
auto a_dbl = A<double>{};
a_int.int_only(0);
// a_dbl.int_only(3.14); error: call to deleted member function
}
https://en.cppreference.com/w/cpp/language/function#Deleted_functions
If I have
template<class T>
TalkyBuffer& operator<<(T const &object) { // Template
...
}
TalkyBuffer& operator<<(TalkySerialisable const &object); // Override
and a class
class A : public TalkySerialisable {
...}
Then if I perform
TalkyBuffer b;
A test;
b << test;
Then gcc is calling the Template function rather than the Override function
However if I specifically define an override
TalkyBuffer& operator<<(A const &object); // Override without polymorphism
Then gcc picks that one.
Is there a practical way to override a templated function with an abstract class?
I read this but it doesn't shed light onto what happens when you throw polymorphism into the mix:
http://www.gotw.ca/publications/mill17.htm
Also I couldn't find a solution here but perhaps I'm using the wrong terms.
When defining the function TalkyBuffer& operator<<(TalkySerialisable const &object); You are not overriding. You are overloading the tmeplated function.
But, when the complier sees b << test;, it searches for an operator that wants an A. It has one, it's the templated function that requires no automatic cast. This is the best choice.
The overloaded function requires an automatic cast (from A to TalkySerialisable) on the parameters to fit the declaration, and is not the best choice.
I think it's possible to use a simple function based solution, reusing function overload for derivation.
struct specialized {};
struct generic {};
template <class T>
TalkyBuffer& serialize(TalkyBuffer& buffer, T const& object, generic) {
...
}
generic dispatch(...) {} // always picked up last in overload resolution
template <class T>
TalkyBuffer& TalkyBuffer::operator<<(T const& object) { // Template
return serialize(*this, object, dispatch(object));
}
Now, let's implement your custom class:
TalkyBuffer& serialize(TalkyBuffer& buffer,
TalkySerialisable const& object,
specialized);
specialized dispatch(TalkySerialisable const&) {}
And create a derived one:
class A: public TalkySerialisable {};
So, what happens ?
TalkyBuffer::operator<<(T const&) will be picked up
when trying to resolve the overload for serialize, it will first compute the result of dispatch
when resolving the result of dispatch, dispatch(TalkySerializable const&) is a better match than dispath(...), thus the return type is specialized
the generic serialize cannot be used (there is no conversion from specialized to generic), so inheritance kicks in
A solution using Boost.enable_if:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>
template<typename T>
typename boost::disable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object) { // Template for non TalkySerializable
...
}
template <typename T>
typename boost::enable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object); // Template overload for TalkySerializable
...
TalkyBuffer b;
A test;
b << test; // calls operator<< <A>(A const &), which instantiates
// the overload for TalkySerializable
b << 41; // calls operator<< <int>(int const &), which corresponds to
// the "default" overload
I'm not sure this is the best solution, but I failed to find a better one: specializing the template does not work either.
As #Matthieu noted in the comment, the previous solution has the major drawback that the base template needs to know that it will be overloaded, which is an unnecessary coupling that hinders extensibility.
To solve this problem, I came up with a new approach using tag dispatching, along with trait classes and compile-time introspection using Boost.MPL macros.
// TalkyBuffer.hpp
#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/has_xxx.hpp>
// defines a metafunction has_talky_buffer_tag<T> that allows us to know at
// compile-time if T has a member type named talky_buffer_tag
BOOST_MPL_HAS_XXX_TRAIT_DEF(talky_buffer_tag)
// tag for the default case
struct default_talky_buffer_tag {};
// trait class for obtaining the tag of a type
template <typename T, typename Enable = void >
struct talky_buffer_trait
{
typedef default_talky_buffer_tag type;
};
// specialization for types that provide a nested typedef
template <typename T>
struct talky_buffer_trait<T,
typename boost::enable_if<has_talky_buffer_tag<T> >::type>
{
typedef typename T::talky_buffer_tag type;
};
struct TalkyBuffer
{
// Insertion operator, which calls an implementation function that can
// be overloaded depending on the tag
template<typename T>
TalkyBuffer & operator<<(T const & object)
{
typename talky_buffer_trait<T>::type tag;
return insertionOperatorImpl(*this, object, tag);
}
};
// default implementation
template <typename T>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, T const & object,
default_talky_buffer_tag)
{
std::cout << "default";
return buf;
}
//-------
// TalkySerializable.hpp
struct TalkySerializable
{
struct tag {};
typedef tag talky_buffer_tag;
};
// A inherits from the nested typedef
struct A : public TalkySerializable {};
// implementation for TalkySerializable objects
template <typename Serializable>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, Serializable const & object,
TalkySerializable::tag)
{
std::cout << "specialized";
return buf;
}
//-------
int main()
{
TalkyBuffer b;
A test;
b << test; // outputs "specialized"
b << 41; // outputs "default"
}
To provide new implementations of the insertion operator for a given type T, one needs to provide a new type to act as a tag (TypeSerializable::tag in our example), provides a way to associate T with the new tag (either by using a nested typedef as in the example, or by specializing the trait class: template <> talky_buffer_trait<T> { typedef new_tag type };), and finally overload the implementation function (insertionOperatorImpl in the example).