My goal is to have a struct that takes in an alias to a specialized enable_if_t<> along with a typename variadic parameter pack and then tells me whether the enable_if's conditions were satisfied for all of the types in the pack. I have a bunch of these specialized enable_ifs, but need to write tests for them before we can put them into our open source project. I have about 2000+ lines of code manually testing these specializations, but bet I can get it to 100 or 200 if I can figure out the pattern below. I have a working version (+ godbolt link), but tbh I'm not sure why it's working and that scheme is breaking in a case where the implementation receives a parameter pack
Here is an example of the code I would like to write and it's result. I'm using C++14 and can steal basic implementations of things from C++17 likes conjunction and void_t
#include <type_traits>
#include <string>
// enable_if for arithmetic types
template <typename T>
using require_arithmetic = typename std::enable_if_t<std::is_arithmetic<T>::value>;
const bool true_arithmetic = require_tester<require_arithmetic, double, int, float>::value;
// output: true
// If any of the types fail the enable_if the result is false
const bool false_arithmetic = require_tester<require_arithmetic, double, std::string, float>::value;
// output: false
The below does do what I want, but tbf I'm not really understanding how.
// Base impl
template <template <class> class Check, typename T1, typename = void>
struct require_tester_impl : std::false_type {};
// I'm not totally sure why void_t needs to be here?
template <template <class> class Check, typename T1>
struct require_tester_impl<Check, T1, void_t<Check<T1>>> : std::true_type {};
// The recursive version (stolen conjuction from C++17)
template <template <class> class Check, typename T = void, typename... Types>
struct require_tester {
static const bool value = conjunction<require_tester_impl<Check, T>,
require_tester<Check, Types...>>::value;
};
// For the end
template <template <class> class Check>
struct require_tester<Check, void> : std::true_type {} ;
In particular, I'm not sure why the void_t is needed in the impl partial specialization for std::true_type.
What I would like to get to is a require_variadic_tester that takes in a variadic templated alias, something like enable_if<conjunction<check<T...>>::value>, and gives me true or false. Sadly, the below returns false no matter what types come in
// impl
template <template <class...> class Check, typename... Types>
struct require_variadic_impl : std::false_type {};
// Adding void_t here causes the compiler to not understand the partial specialiation
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, Check<Types...>> : std::true_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_tester : require_variadic_impl<Check, Types...> {};
I would like the following given the input, but can't seem to shake how to hide that conjunction one level lower
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
require_variadic_tester<require_all_arithmetic, double, double, double>::value;
// is true
require_variadic_tester<require_all_arithmetic, double, std::string, double>::value;
// is false
I think my failure to understand void_t in the first meta function is causing my misunderstanding
Below is the godbolt, any help in understanding this is very appreciated!
https://godbolt.org/z/8XNqpo
Edit:
To give more context in why I want the above with the conjunction inside of the enable_if_t. I'm stuck on C++14 but we are adding a new feature to our open source math library which without more generic types (and requirements on those generic types) we will end up with a ton of code bloat. We currently have stuff like this
template <int R, int C>
inline Eigen::Matrix<double, R, C> add(
const Eigen::Matrix<double, R, C>& m1, const Eigen::Matrix<double, R, C>& m2) {
return m1 + m2;
}
I'd like to have more generic templates and do something like this
template <typename Mat1, typename Mat2,
require_all_eigen<is_arithmetic, Mat1, Mat2>...>
inline auto add(Mat1&& m1, Mat2&& m2) {
return m1 + m2;
}
I have all of those require_*_<container> aliases setup, but the tests for all of those requires is about 2000+ lines and in the future that will be a funky mess to have to deal with.
We have unary and variadic template enable_if aliases, at this point the above unary case does what I want ala a nice test like
#include <gtest/gtest.h>
TEST(requires, arithmetic_test) {
EXPECT_FALSE((require_tester<require_arithmetic, std::string>::value));
EXPECT_TRUE((require_tester<require_arithmetic, double, int, float>::value));
}
The issue I have is with testing the variadic template enable_if aliases, where I want to be able to write something like
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
/// For the tests
TEST(requires, arithmetic_all_test) {
EXPECT_FALSE((require_variadic_tester<require_all_arithmetic, std::string,
Eigen::Matrix<float, -1, -1>>::value));
EXPECT_TRUE((require_variadic_tester<require_all_arithmetic,
double, int, float>::value));
}
If I can test all of this I think the requires part of our library alone could be a nice header only mini-library for what I'm calling "bad fake concepts in 14" (or bfc14 for short ;-))
Here's what happens with your require_tester<require_arithmetic, double, double, int>:
This doesn't match the partial specialization of require_tester, which has just two template arguments <Check, void>, so we use the primary template
template <template <class> class Check, typename T, typename... Types>
struct require_tester;
with Check = require_arithmetic; T = double; Types = double, int. It does not match the partial specialization of require_tester. Member value is the result of
conjunction<require_tester_impl<Check, T>, require_tester<Check, Types...>>::value
where the interesting part is require_tester_impl<Check, T> = require_tester_impl<require_arithmetic, double>. First, since the template parameters of require_tester_impl are
template <template <class> class Check, typename T1, typename = void>
and only two explicit template argumetns are given, we know the actual template arguments are <require_arithmetic, double, void>. Now we need to see whether or not this matches the partial specialization of require_template_impl, so we try to match:
require_template_impl<require_arithmetic, double, void>
require_template_impl<Check, T1, void_t<Check<T1>>>
So template argument deduction finds Check = require_arithmetic and T1 = double. The type void_t<Check<T1>> does not cause any deduction of Check or T1. But the deduced parameter values must be substituted in, and we find void_t<Check<T1>> is void_t<require_arithmetic<double>> is void. This does match the void from the template arguments, so the partial specialization does match, and require_template_impl<require_arithmetic, double, void> inherits std::true_type, not std::false_type.
On the other hand, if T1 were std::string instead of double, substituting the deduced template arguments in would find void_t<require_arithmetic<std::string>> is invalid, via the eventual enable_if<...>::type where no member type exists. When substituting deduced template arguments into other template parameters fails, this means the partial specialization is thrown out as not a match. So require_template_impl<require_arithmetic, std::string, void> uses the primary template and inherits std::false_type.
Going back to the value member of require_tester, it recursively finds require_tester<require_arithmetic, double, int>::value via require_tester<require_arithmetic, int>::value via require_tester<require_arithmetic>::value which is the same as require_tester<require_arithmetic, void>::value. All the value members are true, so the final value is true.
Though I would simplify this a bit:
The void is unnecessary in the require_tester recursion, and causes the strange "fact" that require_tester<Anything, void>::value is always true. It would be better to remove the = void default from the primary require_tester template, and make the base case template <template <class> class Check> require_tester<Check> instead.
Your value expression in the require_tester primary template is always giving exactly two template arguments to conjunction, so it's not really using its variadic property, and you could just as well write require_tester_impl<...>::value && require_tester<...>::value. Since require_tester is doing a recursion itself, it doesn't need the recursive definition abstracted into conjunction. Instead, require_tester could be simplified to count on conjunction and avoid doing any recursion itself:
template <template <class> class Check, typename... Types>
struct require_tester : conjunction<require_tester_impl<Check, Types>...>
{};
// No other specialization needed.
The require_variadic_tester template can follow a similar pattern, except that I'll give the dummy template parameter which was just typename = void a name, typename Enable. And it needs to come before the template parameter pack, so it's not that useful to actually default it to void, and we need to make sure to use the appropriate void template argument in the corresponding position.
template <template <class...> class Check, typename Enable, typename... Types>
struct require_variadic_impl : std::false_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, void_t<Check<Types...>>, Types...> : std::true_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_tester : require_variadic_impl<Check, void, Types...> {};
See the modified program on godbolt, with desired results.
Not sure to understand all your needs but...
What I would like to get to is a require_variadic_tester that takes in a variadic templated alias, something like enable_if<conjunction<check<T...>>::value>, and gives me true or false. Sadly, the below returns false no matter what types come in
Are you sure that you want conjunction<check<T...>> ?
Or do you want conjunction<check<T>...>?
I mean... the check must receive a variadic list of types or do you want to check a an alias, that (as in your example) receive a single type and a conjunction that is true iff (if and only if) the check is satisfied for all types?
In this second case, std::void_t is very handy to verify that all checks are satisfied.
I propose the following require_variadic_impl and require_variadic_tester
template <template <typename> class, typename, typename = void>
struct require_variadic_impl
: public std::false_type
{ };
template <template <typename> class C, typename ... Ts>
struct require_variadic_impl<C, std::tuple<Ts...>, std::void_t<C<Ts>...>>
: public std::true_type
{ };
template <template <typename> class C, typename ... Ts>
struct require_variadic_tester
: public require_variadic_impl<C, std::tuple<Ts...>>
{ };
Now from
template <typename T>
using require_arithmetic = typename std::enable_if_t<std::is_arithmetic<T>::value>;
// ...
printf("\nGeneric Variadic: \n\n");
const char* string_generic_var_check =
require_variadic_tester<require_arithmetic, std::string>::value ? "true" : "false";
const char* double_generic_var_check =
require_variadic_tester<require_arithmetic, double, double, double>::value ? "true" : "false";
std::printf("\t String: %s\n", string_generic_var_check);
std::printf("\t Double: %s\n", double_generic_var_check);
you get
Generic Variadic:
String: false
Double: true
think my failure to understand void_t in the first meta function is causing my misunderstanding
Try thinking std::void_t<Ts...> as "enable if all Ts are enabled".
template <template <class> class Check, typename T1, typename = void>
struct require_tester_impl : std::false_type {};
// I'm not totally sure why void_t needs to be here?
template <template <class> class Check, typename T1>
struct require_tester_impl<Check, T1, void_t<Check<T1>>> : std::true_type {};
Here, you required the third parameter of require_tester_impl is of type void, since you wrote it as default value. If the user, when specializing require_tester_impl doesn't specify its third parameter, it is void. So the compiler will search for a partial specialization where the first template parameter is a unary class template, the second template parameter is a type, and the third one is void, otherwise, no partial specialization will be found, since the third parameter of any partial specialization will fail.
That's where void_t comes into play. Since you want to inject Check into the parameter, but you require void, that's when void_t comes handy, since every type used to specialized it is mapped to void, which is what you really need. When the partial specialization doesn't fail, you will have two enabled specializations, the default one, and the partial one.
The partial one will be finally choosen since it is more specialized than the other, since void have been calculated in a way dependant on other template parameters.
That's for the first part. For the second part (the variadic template), remember that, if enable_if succeds, it returns void.
So your require_variadic_impl:
template <template <class...> class Check, typename... Types>
struct require_variadic_impl : std::false_type {};
// Adding void_t here causes the compiler to not understand the partial specialiation
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, Check<Types...>> : std::true_type {};
have a problem here, and it's that, Check<Types...>, since it is aliased to enable_if, returns void when it success, however, the second parameter of require_variadic_impl is not void so the partial specializations finally fails when the check is correct. When it isn't, then the enable_if have no inner type defined,the partial specialization also fails, and the base case is used again.
However, do it simple. I propose here a much more readable implementation with same final result:
#include <iostream>
#include <type_traits>
#include <string>
template<class... Ts>
struct require_all_arithmetic : std::conjunction<std::is_arithmetic<Ts>...>
{};
template<template<class...> class Check, class... Ts>
struct require_variadic_tester : Check<Ts...>
{};
int main()
{
std::cout << require_variadic_tester<require_all_arithmetic, double, double, double>::value << std::endl;
std::cout << require_variadic_tester<require_all_arithmetic, double, std::string, double>::value << std::endl;
}
https://coliru.stacked-crooked.com/a/f9fb68e04eb0ad40
Or just:
#include <iostream>
#include <type_traits>
#include <string>
template<class... Ts>
struct require_all_arithmetic : std::conjunction<std::is_arithmetic<Ts>...>
{};
int main()
{
std::cout << require_all_arithmetic<double, double, double>::value << std::endl;
std::cout << require_all_arithmetic<double, std::string, double>::value << std::endl;
}
However, if you require a check that is sfinae-friendly, plus a struct that maps "sfinae"-friendly checks to true/false, you can use constexpr methods instead. It's much more simple:
template<class... Ts>
using require_all_arithmetic = std::enable_if_t<std::conjunction<std::is_arithmetic<Ts>...>::value>;
template<template<class...> class Check, class... Ts, class = Check<Ts...> >
constexpr bool require_variadic_tester_impl(int)
{ return true; }
template<template<class...> class Check, class... Ts>
constexpr bool require_variadic_tester_impl(unsigned)
{ return false; }
template<template<class...> class Check, class... Ts>
struct require_variadic_tester
{ static constexpr bool value = require_variadic_tester_impl<Check, Ts...>(42); };
int main()
{
std::cout << require_variadic_tester<require_all_arithmetic, double, double, double>::value << std::endl;
std::cout << require_variadic_tester<require_all_arithmetic, double, std::string, double>::value << std::endl;
}
The technique works as follow: if Check fails, only the second overload will compile, which returns false. However, if the check is valid and the inner enable_if is defined, then both overloads will be valid but, since you have passed an int (42), and the second overload receives an unsigned, the first overload will be a better match, returning true.
https://coliru.stacked-crooked.com/a/bfe22ea099dd5749
Finally, if you want the check always is true_type or false_type, then, instead of inheriting, you can just alias std::conditional:
template<template<class...> class Check, class... Ts>
using require_variadic_tester =
std::conditional_t<require_variadic_tester_impl<Check, Ts...>(42),
std::true_type, std::false_type>;
Here is MCVE (uncompilable) :-
#include <iostream>
#include <type_traits>
//-- library ---
template<class T,template<class>class Slot,class DefaultType>
class GetType{
template <typename C> static Slot<T> check( Slot<T>*);
template <typename> static DefaultType check(...);
public: using type=decltype(check<T>());
};
template<class T,template<class>class Slot,class DefaultType>
using X = typename GetType<T,Slot,DefaultType>::type;
Here is its usage :-
//--- user defined ---
class B {public: using MyType=int;};
class C{};
template<class T> using SlotCustom = typename T::MyType;
int main(){
using ShouldInt=X< B ,SlotCustom ,long>; //B::Mytype =int , result:int
using ShouldLong=X< C ,SlotCustom ,long>;//C::Mytype not exist, result:long
std::cout<< std::is_same_v<ShouldInt, int> <<std::cout; //should true
std::cout<< std::is_same_v<ShouldLong, long> <<std::cout; //should true
}
My objective is to create a library typedef X< Param1 ,SlotCustom ,DefaultType> that means as the following pseudo code:-
if ( SlotCustom<Param1> has meaning) return "SlotCustom<Param1>" ;
else return "DefaultType"; //i.e. by default
How to do it?
Here is a similar question.
The main difference is that X<T> there can be only a bool, and many things are hardcoded.
I am new to template specialization. The solution might be obvious, but I can't find it.
If I understand your question correctly, then your approach can be made to work, for example
template <template <class> class Slot, class DefaultType>
struct GetType
{
template <typename T>
static Slot<T>&& deduce(T&&);
static DefaultType&& deduce(...);
template <typename T>
using type = std::remove_reference_t<decltype(deduce(std::declval<T>()))>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<Slot, DefaultType>::template type<T>;
live demo here
The problem with your initial attempt was that the call to your check function in the expression for decltype() needed some argument for overload resolution to take place so that the SFINAE magic can happen. My example above relies on std::declval to introduce a dummy argument of the necessary type. Also, note that my helper functions use references rather than passing the types by value directly. This is so that it also works with types that are not copyable. Note that there will be problems if Slot<T> or the DefaultType are reference types themselves. One would have to, e.g., introduce additional wrapper types to deal with that…
Alternatively, you could use partial class template specialization to pick the correct type, for example:
template <class T, template <class> class Slot, class DefaultType, typename = void>
struct GetType
{
using type = DefaultType;
};
template <class T, template <class> class Slot, class DefaultType>
struct GetType<T, Slot, DefaultType, std::void_t<Slot<T>>>
{
using type = Slot<T>;
};
template <class T, template <class> class Slot, class DefaultType>
using X = typename GetType<T, Slot, DefaultType>::type;
live demo here
The trick here lies in the use of the last template parameter with default argument void. Due to the way the matching of partial class template specializations works (see, e.g., this answer), the specialization will only be picked if Slot<T> is a valid type. Note that above solution requires C++17. If you have to stay within C++14 (which you probably don't, given that your own example relies on C++17), you can, e.g., provide your own implementation of void_t (as explained here):
template <typename... T> struct make_void { using type = void; };
template <typename... T> using void_t = typename make_void<T...>::type;
Say I have a template class like so:
template < typename TParam >
class Test
{
// content
};
I want to pull out the first template parameter of TParam if it's a specialization of a class template. Something like:
template < typename TParam >
class Test
{
using TParamInner = TemplateType<TParam>::Type;
// use TParamInner here
};
Additional info:
I have access to all of C++98.
I have access to a subset of C++11.
I would prefer to avoid the stdlib if possible (assume this is
because I'm using an embedded system for which no stdlib is available and/or because I am heavily memory-constrained)
You can get close with something like:
template <class >
struct first_template_param;
template <template <class...> class Z, class T, class... Ts>
struct first_template_param<Z<T, Ts...>> {
using type = T;
}
It won't handle std::array or any other class templates that take non-type template parameters. But it'll handle all the "normal" class templates. You can always then add extra specializations for all the ones you want:
template <class T, size_t N>
struct first_template_param<std::array<T,N>> {
using type = T;
}
Thanks to #Barry for spurring the solution along.
It's not a complete answer for all template types, but it works for templates where all parameters are types, which is a large number of the most useful templates.
template < typename Head, typename ... Tail >
struct split { using first = Head; };
template <class >
struct cls_template_info; // fails on non-templates
template <template <class...> class Z, class... Ts>
struct cls_template_info<Z<Ts...>>
{
using type = typename split<Ts...>::first; // typename used to disambiguate
};
This can then be used as using T = cls_template_info<std::vector<int>>::first;.
You can't. A template type is never carried up to runtime. You have to instantiate it (this leads to a complete new type), and the compiler then generates the needed code to make it appear as if you have defined specifically for the type parameters you specified. Indeed, in old compilers (this has been solved a lot of time ago) when you instantiate a generic type in several compilation units, that lead to several repetitions of the same code in the final program. But as I've said, this has been solved time ago.
I asked a question that has several references to the code:
template <typename...>
using void_t = void;
I believe I have a generally misunderstand alias templates:
Why wouldn't you just evaluate whatever template parameter you're passing into an alias template in an enable_if_t or conditional_t statement?
Is the code above just about doing an enable_if_t on multiple template parameters at once?
Secondly, I believe that I have a specific misunderstanding of the role of void_t. This comment states that the C++17 standard defines void_t. Here's what I don't get:
Isn't void_t just an arbitrary name? If I still have to define template <typename...> using void_t = void; wherever I plan to use void_t what's the point of standardizing an arbitrary name?
In Barry's example from your linked question:
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
void_t is just used to translate the type deduced by decltype to void so that it matches the default argument to the primary template definition. The SFINAE is all taken care of by the decltype expression. You could just as easily do the following:
//use , void() instead of wrapping in void_t
//this uses the comma operator to check the type of the to_string call, then change the type to void
decltype(std::to_string(std::declval<T>()), void())
The former version is much easier to read and void_t doesn't require decltype to work.
If void_t is available in your implementation you don't need to redefine it. When it's standardised it will be available just like any of the other alias templates in the standard.
Think about it this way: if T is int, which has a valid std::to_string overload, deduction will look like this:
has_to_string<int> -> has_to_string<int,void> because of the default argument. So lets look for specializations of has_to_string with those arguments.
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
Okay, that is a partial specialization for some T and some dependent type. Let's work out that type:
void_t<decltype(std::to_string(std::declval<T>()))>
//std::to_string(int&&) is valid and returns a std::string
void_t<std::string>
//void_t changes types to void
void
Now our specialization looks like this:
template<>
struct has_to_string<int,void>
: std::true_type { };
This matches our instantiation of has_string<int,void>, so has_to_string<int> inherits from std::true_type.
Now think about it when T is struct Foo{};. Again, let's work out that dependent type:
void_t<decltype(std::to_string(std::declval<T>()))>
//wait, std::to_string(Foo&&) doesn't exist
//discard that specialization
With that specialization discarded, we fall back to the primary template:
template<typename T, typename = void>
struct has_to_string
: std::false_type { };
So has_to_string<Foo> inherits from std::false_type.
I don't think the shown example really shows what void_t is good for as it only shows one use case, but when you look at
template<typename T>
struct has_to_string<T,
void_t<decltype(std::to_string(std::declval<T>()))>
>
: std::true_type { };
it is not so much different from
template<typename T>
struct has_to_string<T,
decltype(std::to_string(std::declval<T>()), void())
>
: std::true_type { };
And for this statement:
The former version is much easier to read and void_t doesn't require decltype to work.
I think the advantage in readability is quite small and the second part makes no sense, when decltype doesn't work, SFINAE kicks in as expected.
One example where void_t is more useful is the one from the proposal:
// primary template handles types that have no nested ::type member
template< class, class = void_t<> >
struct has_type_member
: std::false_type { };
// specialization recognizes types that do have a nested ::type member
template< class T >
struct has_type_member<T, void_t<typename T::type>>
: std::true_type { }
As you can see, even the primary template uses void_t to increase the readability as it now matches the specialization. That is not strictly necessary, but I like it. The real power comes when you think about the alternatives. Without void_t, the specialization is now more complicated:
template< class T >
struct has_type_member<T, decltype(typename T::type, void())>
: std::true_type { }
wouldn't work as T::type names a type, not an expression. You therefore need
template< class T >
struct has_type_member<T, decltype(std::declval<typename T::type>(), void())>
: std::true_type { }
The whole expression becomes longer, more tricky and it might suffer from edge-cases you forgot to handle. This is where void_t really helps, the other uses are then just a small improvement and they increase consistency.
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.
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:
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.
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