8

I am attempting to define a template that will will specify a storage type given another type T. I'd like to use enable_if to catch all the arithmetic types. Below is my attempt at this which complains the template is redeclared with 2 parameters. I tried adding a 2nd dummy parm to the primary template but get a different error. How can this be done?

#include <string>
#include <type_traits>
template <typename T> struct storage_type; // want compile error if no match
// template <typename T, typename T2=void> struct storage_type; // no joy
template <> struct storage_type<const char *> { typedef std::string type; };
template <> struct storage_type<std::string> { typedef std::string type; };
template <typename T, typename std::enable_if<std::is_arithmetic<T>::value>::type* = nullptr> 
    struct storage_type { typedef double type; };

// Use the storage_type template to allocate storage
template<typename T>
class MyStorage {
  public:
  typename storage_type<T>::type storage;
};

MyStorage<std::string> s;  // uses std::string
MyStorage<const char *> s2; // uses std::string
MyStorage<float> f;  // uses 'double'
5
  • Do note that long double can be larger than double. Also long long is a minimum of 64 bits but could be larger. Commented Mar 8, 2017 at 17:51
  • Fix the obvious stuff first? Commented Mar 8, 2017 at 18:02
  • @πάνταῥεῖ I believe that's what he's asking, how to fix that error. Commented Mar 8, 2017 at 18:04
  • @JustinTime Woin't help much in the long term. Commented Mar 8, 2017 at 18:13
  • My actual usage is somewhat different so I simplified the problem to ask the question by throwing in a simple double type. Have no worries, I'm not trying to stuff a larger type into a smaller allocation. Thanks for the sharp eyes though as that could have been really important and might save someone else reading this.
    – Glenn
    Commented Mar 8, 2017 at 18:43

1 Answer 1

21

You can do this by adding a second parameter to the primary template, then specialising to match it; you were on the right track, but didn't do it correctly.

#include <string>
#include <type_traits>
// template <typename T> struct storage_type;                // Don't use this one.
template <typename T, typename T2=void> struct storage_type; // Use this one instead.
template <> struct storage_type<const char *> { typedef std::string type; };
template <> struct storage_type<std::string> { typedef std::string type; };

// This is a partial specialisation, not a separate template.
template <typename T> 
struct storage_type<T, typename std::enable_if<std::is_arithmetic<T>::value>::type> {
    typedef double type;
};

// Use the storage_type template to allocate storage
template<typename T>
class MyStorage {
  public:
  typename storage_type<T>::type storage;
};

MyStorage<std::string> s;  // uses std::string
MyStorage<const char *> s2; // uses std::string
MyStorage<float> f;  // uses 'double'

// -----

struct S {};

//MyStorage<S> breaker; // Error if uncommented.

And voila.

Not the answer you're looking for? Browse other questions tagged or ask your own question.