Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a static_assert to determine if a specific template parameter is a specific untyped class template

Tags:

c++

c++11

I'd like to have a function that restricts the parameters to be only types that derive from a specific templated class. In this case, basic_string (from the STL-docs). For example, a wstring is declared:

typedef basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >
wstring;

The basic idea would be something like this:

template <class TString>
void strings_only_please(TString message) {
    static_assert(is_base_of<basic_string, TString>::value, 
        "Not a string type!");  
}

Of course, that doesn't compile though as basic_string hasn't been specified ... it needs a real type. (While I could likely just hard code the few actual string types, I'm looking for a general solution to this pattern.)

I'm using Visual Studio 2012 and would ideally like the code to be portable to other modern C++ compilers, like GCC.

like image 577
WiredPrairie Avatar asked Dec 27 '22 00:12

WiredPrairie


1 Answers

There are three ways of solving your problem, one would be an implementation of is_specialization_of, the other involves making your function take a std::basic_string<T1,T2,T3> instead of TString, and the third has the same philosophy as the 2nd solution; make a template matchable only by std::basic_string.


is_base_of isn't sufficient in your example because of two reasons:

  1. is_base_of is used to see if type U is derived from T (or if it's the same type), in your snippet there is no inheritance involved.

  2. std::basic_string isn't a complete type and therefore can't be used with is_base_of at all (which you already pointed out).


solution #1

is_specialization_of would be used to check whether type U is a specialization of the incomplete type T. It's quite easy to implement it using a template-template class, as in the below example.

as noted by @SebastianRedl variadic templates are not available using VS2012, see the other solutions (which are not as generic but still sufficient to your needs).

#include <type_traits>
#include <iostream>
#include <string>

template<template<typename...> class T, typename U>
struct is_specialization_of              : std::false_type { };

template<template<typename...> class T, typename... Ts> 
struct is_specialization_of<T, T<Ts...>> : std::true_type  { };

int
main (int argc, char *argv[])
{
  std::cerr << is_specialization_of<std::basic_string, std::string >::value << std::endl;
  std::cerr << is_specialization_of<std::basic_string, std::wstring>::value << std::endl;
  std::cerr << is_specialization_of<std::basic_string, std::istream>::value << std::endl;
}

output

1
1
0

solution #2

template <typename T1, typename T2, typename T3>
void strings_only_please(std::basic_string<T1,T2,T3>) {
  // ...  
}

Sure, the above won't result in a nice static_assert error - but it is sufficient for your needs and does what you want; the function is only callable by types who specialize std::basic_string.


solution #3

template<typename T>
struct is_basic_string : std::false_type { };

template<typename T1, typename T2, typename T3>
struct is_basic_string<std::basic_string<T1,T2,T3>> : std::true_type { };

...

is_basic_string<std::string >::value // true
is_basic_string<std::istream>::value // false
like image 189
Filip Roséen - refp Avatar answered Feb 09 '23 01:02

Filip Roséen - refp