Is it possible to overload or specialize class member functions based on given enum values?
enum class Type {
TypeA,
TypeB,
TypeC
};
class Foo {
public:
template <Type t, typename R = std::enable_if_t<t==Type::TypeA, int>>
R get() {
return 1;
}
template <Type t, typename R = std::enable_if_t<t==Type::TypeB, double>>
R get() {
return 2;
}
template <Type t, typename R= std::enable_if_t<t==Type::TypeC, float>>
R get() {
return 3;
}
};
Foo foo;
std::cout << foo.get<Type::TypeA>() << std::endl;
std::cout << foo.get<Type::TypeB>() << std::endl;
std::cout << foo.get<Type::TypeC>() << std::endl;
The compile complain about overloading on above code snippet.
One pretty standard way to fix it is to put the std::enable_if clause in the return type of the function, rather than in the template parameters like that.
This compiles for me at c++11 standard.
#include <iostream>
#include <type_traits>
enum class Type {
TypeA,
TypeB,
TypeC
};
class Foo {
public:
template <Type t>
typename std::enable_if<t==Type::TypeA, int>::type get() {
return 1;
}
template <Type t>
typename std::enable_if<t==Type::TypeB, double>::type get() {
return 2;
}
template <Type t>
typename std::enable_if<t==Type::TypeC, float>::type get() {
return 3;
}
};
static_assert(std::is_same<int, decltype( std::declval<Foo>().get<Type::TypeA>())>::value, "");
static_assert(std::is_same<double, decltype( std::declval<Foo>().get<Type::TypeB>())>::value, "");
static_assert(std::is_same<float, decltype( std::declval<Foo>().get<Type::TypeC>())>::value, "");
int main() {
Foo foo;
std::cout << foo.get<Type::TypeA>() << std::endl;
std::cout << foo.get<Type::TypeB>() << std::endl;
std::cout << foo.get<Type::TypeC>() << std::endl;
}
I'm not sure if I can explain in detail why this change makes such a difference for the compiler.
However, consider the following. With your version, although you are never actually instantiating get with two explicit template parameters, technically all three of those member functions templates can "collide" and produce functions with exactly the same name. Because, if you did instantiate get<Type::TypeB, int>, then it would have the same return type, input parameters, and name as get<Type::TypeA>. C++ does not support function template specialization, it would make overload resolution rules very complicated. So having function templates with the potential to collide like this can make the compiler very upset.
When you do it the way I showed, there is no possibility that the templates can collide and produce a function with the same name and signature.
You can avoid the use of std::enable_if mapping the Type and return types specializing a simple struct (Bar, in the following example)
#include <iostream>
enum class Type {
TypeA,
TypeB,
TypeC
};
template <Type t>
struct Bar;
template <>
struct Bar<Type::TypeA>
{ using type = int; };
template <>
struct Bar<Type::TypeB>
{ using type = double; };
template <>
struct Bar<Type::TypeC>
{ using type = float; };
class Foo {
public:
template <Type t>
typename Bar<t>::type get();
};
template <>
Bar<Type::TypeA>::type Foo::get<Type::TypeA> ()
{ return 1; }
template <>
Bar<Type::TypeB>::type Foo::get<Type::TypeB> ()
{ return 2.2; }
template <>
Bar<Type::TypeC>::type Foo::get<Type::TypeC> ()
{ return 3.5f; }
int main ()
{
Foo foo;
std::cout << foo.get<Type::TypeA>() << std::endl;
std::cout << foo.get<Type::TypeB>() << std::endl;
std::cout << foo.get<Type::TypeC>() << std::endl;
return 0;
}
Your example doesn't compile because defaulted type templated parameters are not part of the function signature. So as far as the compiler is concerned, you are defining the same function multiple times, which is illegal. Instead, you need to use a defaulted non-type template parameter.
class Foo {
public:
template <Type t, std::enable_if_t<t==Type::TypeA, int> = 0>
int get() {
return 1;
}
template <Type t, std::enable_if_t<t==Type::TypeB, int> = 0>
double get() {
return 2;
}
template <Type t, std::enable_if_t<t==Type::TypeC, int> = 0>
float get() {
return 3;
}
};
Two working solutions are already posted; so why did I post this one? Well, I like it better. To be concrete:
It keeps what the function actually takes and returns separate from when it's enabled. This makes the function easier to read and understand. You can pull the code snippet from the second argument into a tiny macro called REQUIRE or similar; this makes it extremely clear what's going on. Neither of the other two answers posted have this property.
This technique is more universal; it can be used in slightly different situations with constructors, which don't return anything and cannot be used with the other two solutions
max66's approach of mapping the enum to a type is nice and it's something you should be aware of. It's more appropriate though when you can write the body generically; if you have to write out every implementation body separately anyhow, it only adds boilerplate (IMHO). Also you should be aware that a solution based on specialization is fragile; it doesn't work if there's a second template parameter nor if the class Foo is a class template, because of the restrictions on specialization function templates.
With the macro I mentioned above you could write it this way:
template <Type t, REQUIRE(t==Type::TypeA)>
int get() {
return 1;
}
// ...
Which I think is as good as it gets.
Related
I am trying to figure out how to "overload" a variadic function template with a "more specialized" variadic function template. For example:
#include <iostream>
template <typename... ARGS_>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
struct Test {
};
template <typename... ARGS_>
void foo(void(*fn)(ARGS_..., Test*)) {
std::cout << "Test function pointer foo" << std::endl;
}
void test1(int a, int b) {
std::cout << "test1()" << std::endl;
}
void test2(int a, int b, Test* x) {
std::cout << "test2()" << std::endl;
}
int main() {
foo(&test1);
foo(&test2);
return 0;
}
The output of this code is:
Generic function pointer foo
Generic function pointer foo
Rather than:
Generic function pointer foo
Test function pointer foo
as I want.
Conceptually, I am trying to notate "Use template method A if you have any type(s) of arguments where the LAST on is Test* and use template method B if the last type is NOT Test*."
What is the correct method to accomplish this type of behavior?
SFINAE'd overloads based on the last parameter pack argument
You can add mutually exclusive overloads based on whether the last type in the variadiac parameter pack is Test* or not:
#include <type_traits>
template <typename... Ts>
using last_t = typename decltype((std::type_identity<Ts>{}, ...))::type;
struct Test {};
template <
typename... ARGS_,
std::enable_if_t<!std::is_same_v<last_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
template <
typename... ARGS_,
std::enable_if_t<std::is_same_v<last_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
// Special case for empty pack (as last_t<> is ill-formed)
void foo(void (*fn)()) { std::cout << "Zero args" << std::endl; }
making use if C++20's std::type_identity for the last_t transformation trait.
Used as:
void test1(int, int b) {}
void test2(int, int b, Test *x) {}
void test3(Test *) {}
void test4() {}
int main() {
foo(&test1); // Generic function pointer foo
foo(&test2); // Test function pointer foo
foo(&test3); // Test function pointer foo
foo(&test4); // Zero args
}
Avoiding the zero-arg special case as an overload?
The zero-arg foo overload can be avoided in favour of tweaking the last_t trait into one which also accepts an empty pack, such that a query over the empty pack is used to resolve to the generic overload. Neither its semantics nor its implementation becomes as straight-forward and elegant, however, as "the last type in an empty type list" does not make much sense, meaning the trait need to be tweaked into something different:
template <typename... Ts> struct last_or_unique_dummy_type {
using type = typename decltype((std::type_identity<Ts>{}, ...))::type;
};
template <> class last_or_unique_dummy_type<> {
struct dummy {};
public:
using type = dummy;
};
template <typename... Ts>
using last_or_unique_dummy_type_t =
typename last_or_unique_dummy_type<Ts...>::type;
template <typename... ARGS_,
std::enable_if_t<!std::is_same_v<
last_or_unique_dummy_type_t<ARGS_...>, Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Generic function pointer foo" << std::endl;
}
template <typename... ARGS_,
std::enable_if_t<std::is_same_v<last_or_unique_dummy_type_t<ARGS_...>,
Test *>> * = nullptr>
void foo(void (*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
Using an additional overload for the empty pack is likely the least surprising approach.
C++20 and the identity_t trick
In case you are not yet at C++20, an identity meta-function is trivial to write yourself:
template <typename T>
struct type_identity {
using type = T;
};
Any specialization of this class template, unless partially/explicitly specialized otherwise (which is UB for the STL type), is trivial and default-constructible. We leverage this in the definition of last_t above: default-constructing a series of trivial types in an unevaluated context, and leveraging that the last of those types embeds the input to the identity trait whose specialization is that trivial type, and whose wrapped alias declaration type is the type of the last parameter in the variadic parameter pack.
Check that the last element of ARGS... is Test*.
It won't do that for you this way. One ways is:
template<class...Ts>
struct last_type {};
template<class T1, class T2, class...Ts>
struct last_type<T1, T2, Ts...>:last_type<T2, Ts...>{};
template<class T>
struct last_type<T>{
using type=T;
};
template<class...Ts>
using last_type_t = typename last_type<Ts...>::type;
now you just:
template <typename... ARGS_>
requires std::is_same_v<last_type_t<ARGS_...>, Test*>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
Live example.
Without concepts, you have to replace that requires clause:
template <typename... ARGS_,
std::enable_if_t<std::is_same_v<last_type_t<ARGS_...>, Test*>, bool> = true
>
void foo(void(*fn)(ARGS_...)) {
std::cout << "Test function pointer foo" << std::endl;
}
which is a more obscure "cargo cult" way to basically say the same thing. (you also need to invert enable if clause in the other overload; not = false but !, and handle 0 arg case (prepend void on the type list?))
The reason why your attempt doesn't work is that C++ makes ... matching insanely greedy. It is generally not a good idea to put things you want pattern matched behind it in a context where pattern matching of parameters is done.
What is the best and most correct way to define a template function whose argument's type is required to be a subclass of a certain base class?
Here is what I have:
class A {};
class B : public A {};
class C {};
template <typename T>
int foo(T& x) // NB: need a solution that allows a return value
{
A& dummy = x; // require T to be a subclass of A, else SFINAE
return 1;
}
//int foo(C&) { return 2; }
int main()
{
B b;
C c;
std::cout << foo(b) << "\n";
std::cout << foo(c) << "\n"; // should fail unless foo(C&) is defined
}
Is the above correct? I don't like it because it doesn't express intent. I would prefer something that read more like the following bogus code:
template <class A T>
void foo(T& x) {}
Maybe enable_if can be used somehow?
(Background: In my production code the functions are operators. I need them to be templates for metaprogramming reasons (to deduce the compile time type of T). But I want to restrict them to only match subclasses of A.)
Update #1: In a related question on Template Constraints C++ The following is given:
static_assert(std::is_base_of<A, T>::value, "T must be a sublass of A");
This captures the intent better than A& dummy = x; however it suffers from the same problem that if void foo(C&) { } is commented out, foo<T> still gets specialized for the call to foo(c) and then the static_assert fails. Whereas I would like for the compiler to not even try to specialize foo<T> for cases where the parameter is not a subclass of A.
Update #2: Based on the accepted answer here: Why does enable_if_t in template arguments complains about redefinitions? the following code appears to be close, however it is C++14 specific. I would like a solution portable to current standards.
template <typename T, std::enable_if_t<std::is_base_of<A, T>::value>* = nullptr>
void foo(T& x)
{
}
The advantage of the above is that when void foo(C&) is defined, the compiler gives the correct error: "error: no matching function for call to 'foo(C&)'".
Depends on the check you actually want to do.
To check for a public, unambiguous base, use is_convertible on pointers:
template <class T>
auto foo(T& x) -> std::enable_if_t<std::is_convertible<T*, A*>{}/*, void*/> {
// stuff
}
To check for any base whatsoever, public, protected or private, ambiguous or unambiguous, use is_base_of:
template <class T>
auto foo(T& x) -> std::enable_if_t<std::is_base_of<A, T>{}/*, void*/> {
// stuff
}
The following is another way to do it, based on T.C.'s answer, and the accepted answer here: (Why does enable_if_t in template arguments complains about redefinitions?):
#include <type_traits>
#include <iostream>
// Provide enable_if_t in C++11
#if __cplusplus <= 201103L
namespace std {
template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
}
#endif
class A {};
class B : public A {};
class C {};
template <typename T, std::enable_if_t<std::is_convertible<T*, A*>::value>* = nullptr>
int foo(T& x)
{
return 1;
}
//int foo(C& x) { return 2; }
int main()
{
B b;
C c;
std::cout << foo(b) << "\n";
std::cout << foo(c) << "\n"; // should fail unless foo(C&) is defined
}
This correctly ignores the templated foo when int foo(C& x) is undefined, and therefore gives the expected (desired) compiler error for the foo(c) line:
error: no matching function for call to 'foo'
I have a number of classes with differing members, all of which have operations of the following type
::basedata::Maindata maindata;
::basedata::Subdata subinfo("This goes into the subinfo vector");
subinfo.contexts(contextInfo);
maindata.subdata().push_back(subinfo);
Note that I am asking how to set up generalized templates to perform these actions. I cannot set up a special case for each type of maindata and subinfo. I also need to be able to see how to call the template from my main code. I have been able to set up a template if maindata.subdata() exists, but keep getting a compilation failure on a call to the template if it does not exist. That is create the template of the form
perform_Push(maindata.subdata(), subinfo);
so that it can be compiled whether or not maindata.subdata() exists or not.
I could accept templates that build so that the main code can show
bool retval
retval = hasmember(maindata, subdata);
if (retval)
{
buildmember(maindata.subdata, subinfo);
setAttributes(subinfo, data);
perform_Push(maindata.subdata(), subinfo)
}
else
{
// Perform alternate processing
}
As of now, the code inside the if would not compile when the templates being called should just be void.
While ::basedata::Maindata is always defined, ::basedata::Subdata may or may not be defined depending on the release of libraries that my code is being build with. subdata is defined as a vector belonging to maindata which therefore has the push_back() operation defined. There are too many types of subData to create a separate template for each type as T::Subdata within a template in any case.
That is, if subdata was the only case, I could create a specialization of the template T as ::maindata::subdata and a generic Template T.
I do not have any control of the include files or library that for this so that I cannot create a #define of a variable to test with the pre-compiler. Is there a good way of setting up a template that would allow this to work? I could use a template that returns a boolean true (success) or false (no such definition) and call the alternate processing at run time. I would not need to have an alternate template.
Basically, I am asking how to apply SFINAE to this particular situation.
I have managed to figure out what I need to do to set up the basic template
If I have the most basic operation
maindata.subdata().push_back(data)
I can define a template of the form,
<template class T, typename D>
auto doPush(D data) -> decltype(T.pushback(data), void())
{
T.push_back(data);
}
and the call would be
doPush<maindata.subdata()>(data);
However, the problem would be how to set it up when maindata does not yet have a member subdata.
You can use this templates to obtain a boolean value that tell you if exist member type Subdata in a generic type T. This works only if T is a struct/class not a namespace.
#include <type_traits>
template <class T, class V = void>
struct hasSubdata
{
enum { value = false };
};
template <class T>
struct hasSubdata<T, typename std::enable_if< std::is_same<typename T::Subdata, typename T::Subdata>::value >::type>
{
enum { value = true };
};
struct basedata1
{
struct Subdata {};
};
struct basedata2
{
};
#include <iostream>
int main ()
{
std::cout << "basedata1: " << hasSubdata<basedata1>::value << std::endl;
std::cout << "basedata2: " << hasSubdata<basedata2>::value << std::endl;
}
But you can't use a normal if because the compiler checks the correctness of all the possibilities.
You have to act in a similar way (pretty ugly):
template <class T, bool = hasSubdata<T>::value>
struct SubdataUser
{
static void foo ()
{
std::cout << "I can use SubData member :)" << std::endl;
typename T::Subdata subinfo ();
}
};
template <class T>
struct SubdataUser<T, false>
{
static void foo ()
{
std::cout << "I can not :(" << std::endl;
}
};
int main ()
{
SubdataUser<basedata1>::foo ();
return 0;
}
Unfortunately to my knowledge, you can not have a template hasMember<Type,Member>::value because if Member does not exist, compilation fails.
But you might like a solution of this type
#include <type_traits>
#include <iostream>
struct basedata1
{
struct Subdata1 {};
struct Subdata2 {};
struct Subdata3 {};
};
struct basedata2
{
struct Subdata1 {};
//struct Subdata2 {};
struct Subdata3 {};
};
template <class...>
struct Require
{
enum { value = true };
};
template <class T, bool = true>
struct Impl
{
static void foo ()
{
std::cout << "At least one of the members required is not available :(" << std::endl;
}
};
template <class T>
struct Impl<T, Require< typename T::Subdata1,
typename T::Subdata2,
typename T::Subdata3 >::value >
{
static void foo ()
{
std::cout << "All members are available :)" << std::endl;
typename T::Subdata2 my_var;
}
};
int main( int argc, char* argv[] )
{
Impl<basedata1>::foo ();
Impl<basedata2>::foo ();
return 0;
}
I hope this helps
I have managed to figure out what I need to do to set up the basic template as well as the member template. It is actually two different questions and two different answer templates. It requires a basic generic template called by a specific member template.
C++ preprocessor test if class member exists
I have noticed that there is an asymmetry between the signature used to distinguish unique template functions, and the signature used to distinguish unique functions (including those instantiated from template functions).
In particular, template functions that differ only by return type are considered to be unique, whereas functions that differ only by return type are considered to be redundant.
Therefore, I have a corresponding question about how to disambiguate between function templates that differ only by return type, at the point of instantiation:
#include <iostream>
template<typename T>
long foo(T)
{
std::cout << "long" << std::endl;
return 0;
}
template<typename T>
char foo(T)
{
std::cout << "char" << std::endl;
return '\0';
}
int main()
{
double d = 0.0;
long n = foo(d); // <- Ambiguous: How to specify the template function to use?
}
In the above code, the instantiation of the template function foo is ambiguous precisely because of the asymmetry I've just mentioned. The presence of the two template function definitions is legal, but the instantiation is illegal, even though the return type is specified in the same line of code.
I am asking this question purely for theoretical learning purposes. Perhaps this code construct, in real life, would be a sign of poor design. Perhaps it would never arise in real life. Also, I can envision different ways of overcoming this issue by changing the template definitions (or by making other changes).
However, I would nonetheless like to know if, keeping the template definitions unchanged, it is possible to disambiguate between these two template functions at the point of instantiation.
When using templates you can actually disambiguate the two different overloads. It ain't pretty but works:
long n = static_cast<long(*)(double)>(&foo)(d);
If you really need to have two function templates having the same names, same list of parameters, but different return types, you have no choice but differentiate the two by making the return type a template parameter:
template <typename R, typename T>
R foo(T);
IIRC there is partial function template specialization in C++11, although I could not find anything about it in the standard. If there is, this should work:
//partial function template specializations: C++11 only!
template <typename T>
long foo<long, T>(T)
{
std::cout << "long" << std::endl;
return 0;
}
template<typename T>
char foo<char, T>(T)
{
std::cout << "char" << std::endl;
return '\0';
}
Or else, in C++03:
template <typename R, typename T>
struct FooImpl;
template <typename T>
struct FooImpl<long, T>
{
static long doIt(T)
{
std::cout << "long" << std::endl;
return 0;
}
};
template <typename T>
struct FooImpl<char, T>
{
static char doIt(T)
{
std::cout << "char" << std::endl;
return '\0';
}
};
template <typename R, typename T>
R foo(T t)
{
return FooImpl<R, T>::doIt(t);
}
In both cases, your main would look like this:
int main()
{
double d = 0.0;
long n = foo<long>(d); // specify the return type only
auto c = foo<char>(n);
}
I have an SFINAE problem:
In the following code, I want the C++ compiler to pick the specialized functor and print "special", but it's printing "general" instead.
#include <iostream>
#include <vector>
template<class T, class V = void>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
template<class T>
struct Functor<T, typename T::Vec> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
struct Foo {
typedef std::vector<int> Vec;
};
int main() {
Functor<Foo> ac;
ac();
}
How can I fix it so that the specialized struct is used automatically? Note I don't want to directly specialize the Functor struct on Foo, but I want to specialize it on all types that have a Vec type.
P.S.: I am using g++ 4.4.4
Sorry for misleading you in the last answer, I thought for a moment that it would be simpler. So I will try to provide a complete solution here. The general approach to solve this type of problems is to write a traits helper template and use it together with enable_if (either C++11, boost or manual implementation) to decide a class specialization:
Trait
A simple approach, not necessarily the best, but simple to write would be:
template <typename T>
struct has_nested_Vec {
typedef char yes;
typedef char (&no)[2];
template <typename U>
static yes test( typename U::Vec* p );
template <typename U>
static no test( ... );
static const bool value = sizeof( test<T>(0) ) == sizeof(yes);
};
The approach is simple, provide two template functions, that return types of different sizes. One of which takes the nested Vec type and the other takes ellipsis. For all those types that have a nested Vec the first overload is a better match (ellipsis is the worst match for any type). For those types that don't have a nested Vec SFINAE will discard that overload and the only option left will be the ellipsis. So now we have a trait to ask whether any type has a nested Vec type.
Enable if
You can use this from any library, or you can roll your own, it is quite simple:
template <bool state, typename T = void>
struct enable_if {};
template <typename T>
struct enable_if<true,T> {
typedef T type;
};
When the first argument is false, the base template is the only option, and that does not have a nested type, if the condition is true, then enable_if has a nested type that we can use with SFINAE.
Implementation
Now we need to provide the template and the specialization that will use SFINAE for only those types with a nested Vec:
template<class T, class V = void>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
template<class T>
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
Whenever we instantiate Functor with a type, the compiler will try to use the specialization, which will in turn instantiate has_nested_Vec and obtain a truth value, passed to enable_if. For those types for which the value is false, enable_if does not have a nested type type, so the specialization will be discarded in SFINAE and the base template will be used.
Your particular case
In your particular case, where it seems that you don't really need to specialize the whole type but just the operator, you can mix the three elements into a single one: a Functor that dispatches to one of two internal templated functions based on the presence of Vec, removing the need for enable_if and the traits class:
template <typename T>
class Functor {
template <typename U>
void op_impl( typename U::Vec* p ) const {
std::cout << "specialized";
}
template <typename U>
void op_impl( ... ) const {
std::cout << "general";
}
public:
void operator()() const {
op_impl<T>(0);
}
};
Even though this is an old question, I think it's still worth providing a couple more alternatives for quickly fixing the original code.
Basically, the problem is not with the use of SFINAE (that part is fine, actually), but with the matching of the default parameter in the primary template (void) to the argument supplied in the partial specialization(typename T::Vec). Because of the default parameter in the primary template, Functor<Foo> actually means Functor<Foo, void>. When the compiler tries to instantiate that using the specialization, it tries to match the two arguments with the ones in the specialization and fails, as void cannot be substituted for std::vector<int>. It then falls back to instantiating using the primary template.
So, the quickest fix, which assumes all your Vecs are std::vector<int>s, is to replace the line
template<class T, class V = void>
with this
template<class T, class E = std::vector<int>>
The specialization will now be used, because the arguments will match. Simple, but too limiting. Clearly, we need to better control the type of the argument in the specialization, in order to make it match something that we can specify as the default parameter in the primary template. One quick solution that doesn't require defining new traits is this:
#include <iostream>
#include <vector>
#include <type_traits>
template<class T, class E = std::true_type>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
template<class T>
struct Functor<T, typename std::is_reference<typename T::Vec&>::type> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
struct Foo {
typedef std::vector<int> Vec;
};
int main() {
Functor<Foo> ac;
ac();
}
This will work for any Vec type that could make sense here, including fundamental types and arrays, for example, and references or pointers to them.
Another alternative for detecting the existence of a member type is to use void_t. As valid partial specialisations are preferable to the general implementation as long as they match the default parameter(s), we want a type that evaluates to void when valid, and is only valid when the specified member exists; this type is commonly (and, as of C++17, canonically) known as void_t.
template<class...>
using void_t = void;
If your compiler doesn't properly support it (in early C++14 compilers, unused parameters in alias templates weren't guaranteed to ensure SFINAE, breaking the above void_t), a workaround is available.
template<typename... Ts> struct make_void { typedef void type; };
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
As of C++17, void_t is available in the utilities library, in type_traits.
#include <iostream>
#include <vector>
#include <type_traits> // For void_t.
template<class T, class V = void>
struct Functor {
void operator()() const {
std::cerr << "general" << std::endl;
}
};
// Use void_t here.
template<class T>
struct Functor<T, std::void_t<typename T::Vec>> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
struct Foo {
typedef std::vector<int> Vec;
};
int main() {
Functor<Foo> ac;
ac();
}
With this, the output is special, as intended.
In this case, since we're checking for the existence of a member type, the process is very simple; it can be done without expression SFINAE or the type_traits library, allowing us to rewrite the check to use C++03 facilities if necessary.
// void_t:
// Place above Functor's definition.
template<typename T> struct void_t { typedef void type; };
// ...
template<class T>
struct Functor<T, typename void_t<typename T::Vec>::type> {
void operator()() const {
std::cerr << "special" << std::endl;
}
};
To my knowledge, this should work on most, if not all, SFINAE-capable C++03-, C++11-, C++14-, or C++1z-compliant compilers. This can be useful when dealing with compilers that lag behind the standard a bit, or when compiling for platforms that don't have C++11-compatible compilers yet.
For more information on void_t, see cppreference.