Why SFINAE requires the 'Enable' class template parameter? - c++

(this question is not related to C++11/C++14: the examples are compiled using C++03)
enable_bool<T> has a member ::type only when T is bool
template <class T>
struct enable_bool
{};
template <>
struct enable_bool< bool >
{ typedef bool type; };
In the next snippet, the partial specialization is correct (see gcc.godbolt.org)
template <class T, class U, class Enable = T>
struct Foo
{
static int bar() { return 0; }
};
template <class T, class U>
struct Foo< T, U, typename enable_bool<T>::type >
{
static int bar() { return 1; }
};
int main()
{
return Foo <int, bool>::bar();
}
As enable_bool<T>::type already corresponds to T (when T is bool)
we are tempted to factorize parameters T and Enable.
But compiler complains (see gcc.godbolt.org)
template <class T, class U>
struct Foo
{
static int bar() { return 0; }
};
template <class T, class U> //ERROR non-deducible template parameter 'T'
struct Foo< typename enable_bool<T>::type, U >
{
static int bar() { return 1; }
};
Why compiler cannot deduce template parameter T in this above partial specialization?

Finally the question was not even related to SFINAE!
Consider this very simple snippet without SFINAE:
enable<T>::type is always same as T whatever the provided T type.
template <class T>
struct enable
{ typedef T type; }; // Enable always
template <class U, class V>
struct Foo
{
static int bar() { return 0; }
};
template <class X, class Y> //ERROR non-deducible parameter 'X'
struct Foo< typename enable<X>::type, Y >
{
static int bar() { return 1; }
};
int main()
{
return Foo<int, bool>::bar();
}
When compiler tries to match Foo<int, bool> with Foo<typename enable<X>::type,Y>
1st param U = int <--> enable<X>::type => Cannot deduce X
2nd param V = bool <--> Y
Compiler is not designed to deduce X from equation int = enable<X>::type.
Therefore, compiler needs some help from developer.
Another parameter is required: Enable.
The below fixed snippet add the Enable class template parameter.
The compiler performs the following matching:
1st param U =int <--> X
2nd param V =bool<--> Y
3rd param Enable=int <--> enable<X>::type (deduced X from 1st param)
(3rd param is int because declaration class Enable=U means by default 3rd param same as 1st one)
Fixed snippet:
template <class T>
struct enable
{ typedef T type; }; // Enable always
template <class U, class V, class Enable = U>
struct Foo
{
static int bar() { return 0; }
};
template <class X, class Y> // Compiler can deduce 'X'
struct Foo< X, Y, typename enable<X>::type >
{
static int bar() { return 1; }
};
int main()
{
return Foo<int, bool>::bar();
}

Related

Template argument deduction with template class inside template class

I have a template class inside another template class. How can I write a template function that will accept any combination of inner/outer template class?
template <class X>
struct A
{
template <class Y>
struct B
{
int q;
};
};
template <class X, class Y>
int f( typename A<X>::template B<Y>& ab )
{
return ab.q;
}
int g( A<char>::B<short>& ab )
{
return f( ab ); // Error: Could not deduce template argument
}
This template accepts any combination of inner and outer:
tempalte <typename T>
int f(T& ab)
{
return ab.q;
}
You cannot deduce X and Y from typename A<X>::template B<Y>. See here for details: What is a nondeduced context?. The fundamental issue is that there is no 1:1 relation between the actual type that the name typename A<X>::template B<Y> refers to and X and Y. Consider that there could be a specialization:
template <>
struct A<double>
{
template <class Y>
using B = A<int>::B<Y>;
};
Now A<double>::B<Y> and A<int>::B<Y> refer to the same type. Given one or the other there is no way to unambiguously deduce X.
The compiler is not allowed to infer the template arguments in this context, so in order to transport the type information, you have to make it explicit.
One possibility is to provide aliases:
template <class X>
struct A
{
template <class Y>
struct B
{
using X_type = X;
using Y_type = Y;
int q;
};
};
template <class T>
int f( T& ab )
{
return ab.q;
}
int g( A<char>::B<short>& ab )
{
return f( ab );
}
This way, you still have access to the types as e.g. typename T::X_type.

again about C++ SFINAE in enable_if_t

I'm trying to figure out why SFINAE doesn't work with a direct template argument initialization?
It works in this form, when I declare another template parameter, here:
#include <iostream>
template <bool B, class T = void>
class enable_if {};
template <class T>
struct enable_if<true, T> { typedef T type; };
template <bool B, class T>
using enable_if_t = typename enable_if<B,T>::type;
template< typename T >
struct Y {
public:
template< typename U = T >
enable_if_t<true, U>
foo() {
return 111;
}
template< typename U = T >
enable_if_t<false, U>
foo() {
return 222;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
(prints out 111)
but if I refactor to this syntax, the compiler gives an error:
#include <iostream>
template <bool B, class T = void>
class enable_if {};
template <class T>
struct enable_if<true, T> {
typedef T type;
};
template <bool B, class T>
using enable_if_t = typename enable_if<B,T>::type;
template< typename T >
struct Y {
template< typename U = enable_if_t<true, T> >
U
foo() {
return 11;
}
template< typename U = enable_if_t<false, T> >
U
foo() {
return 12;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
"Class member cannot be redeclared" I assume the second instance formed invalid and should be excluded by SFINAE?
And why can't I declare foo()'s like this:
enable_if_t<true, T> foo() { return 11; }
enable_if_t<false, T> foo() { return 12; }
based just on the class template's T parameter?
the second enable_if_t should not be valid and the instance of foo() should be discarded, right?
In the second case, you declare two class methods:
template<typename U> U foo();
You can't have two template class methods with the same signature and name just like you can't declare two non-template class methods with the same signature and name:
int bar();
and
int bar();
Your two template class methods have different template parameters defaults, but that is immaterial. Template parameter defaults are not considered to be a part of the signature. SFINAE does not come into play here. SFINAE is something that happens when template substitution occurs, the "S" part, not declaration.

How can i identify if a template parameter argument is instance of another class in a struct within template? C++

I have a .h file with all my templates in it and a .cpp file with my main.
Part of .h templates:
template<int N, int P>
struct BOUND {
static inline int eval(int v) {
//...
return 1;
};
};
template<class K>
struct VAL_x {
static inline int eval(int v) {
//...
return 1;
};
};
template<int L, class K>
struct LIT {
static inline int eval(int v) {
//...
return 1;
};
};
template<class A, class B, class K>
struct ADD {
static inline int comp_b(int v){
// HERE check if class A is LIT or VAL_x
//...
return 2;
};
};
Here is how I call in my main() this template:
int main() {
typedef ADD<VAL_x<BOUND<2,3> >, LIT<2, BOUND<2,3> >, BOUND<2,3> > FORM;
FORM exec_form;
int y = 2;
int bounds = exec_form.comp_b(y);
return 0;
}
How can I know in ADD::comp() function of my struct, if an argument that was passed is instance of a specific class (e.g. LIT<> )? Those arguments can be passed in any order (e.g all arguments could be LIT, or only the second one)
NOTE: there are also other structs apart from VAL_x , LIT, BOUND and ADD.
Option #1
Introduce a separate trait for each class template of interest (C++03 doesn't help here much).
template <bool B> struct bool_constant { static const bool value = B; };
template <bool B> const bool bool_constant<B>::value;
template <typename T> struct is_LIT : bool_constant<false> {};
template <int L, int M> struct is_LIT<LIT<L, M> > : bool_constant<true> {};
template <typename T> struct is_VAL_x : bool_constant<false> {};
template <int K> struct is_VAL_x<VAL_x<K> > : bool_constant<true> {};
template <class A, class B>
struct ADD
{
static inline int comp_b(int v)
{
if (is_LIT<A>::value && is_VAL_x<B>::value)
{
}
return 2;
}
};
DEMO
Option #2
Use a generic custom trait, whose specialization detects if the type passed is an instantiation of the specified template-template parameter (it is if the specialization matches, i.e., T is an instantiation of class template X):
template <template <int> class X, typename T>
struct is_template { static const bool value = false; };
template <template <int> class X, int N>
struct is_template<X, X<N> > { static const bool value = true; };
template <typename A, typename B>
struct ADD
{
static inline int comp_b(int v)
{
if (is_template<VAL_x, A>::value && is_template<LIT, B>::value)
{
}
return 2;
}
};
DEMO 2
Option #3
Use tag-dispatching, possibly add overloads for other class templates that return true/false, making it similar to Option #1. This solution also relies on overload resolution, that prefers more specialized function templates over those less constrained/generic.
template <typename T> struct tag {};
template <typename A, typename B>
struct ADD
{
static inline int comp_b(int v)
{
return comp_b(v, tag<A>(), tag<B>());
}
template <int M, int N>
static inline int comp_b(int v, tag<LIT<M> >, tag<VAL_x<N> >)
{
return 1;
}
template <typename T, typename U>
static inline int comp_b(int v, tag<T>, tag<U>)
{
return 2;
}
};
DEMO 3
You could do it like this:
#include <typeinfo>
...
template<class A, class B>
struct ADD {
static inline int comp_b(int v){
// HERE check if class A is LIT or VAL_x
std::cout << ( typeid(A)==typeid(VAL_x) ) << '\n';
return 2;
};
};
where I am using std::type_info, which will print 1, for true evaluation.
Or, with c++11, you could do:
#include <type_traits>
...
if (std::is_same<A, VAL_x>::value)
std::cout << "they are same!\n";
However, you could overload a function or such. Make sure you read this: How do I check my template class is of a specific classtype? and this How to check for the type of a template parameter?

Correct usage of C++ template template parameters

I've some trouble to make use of template template parameters. Here is a very simplified example:
template <typename T>
struct Foo {
T t;
};
template <template <class X> class T>
struct Bar {
T<X> data;
X x;
};
int main()
{
Bar<Foo<int>> a;
}
The compiler (g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2) reports the following error:
main.cpp:8:5: error: ‘X’ was not declared in this scope
T<X> data;
^
main.cpp:8:6: error: template argument 1 is invalid
T<X> data;
^
Any idea what's wrong?
So I would like so make use of something like Bar<Foo<>>
template <typename T = int>
struct Foo {
T t;
};
template <typename T>
struct Baz {
T t;
};
template <typename T>
struct Bar;
template <template <typename> class T, typename X>
struct Bar<T<X>> {
T<X> data;
X x;
};
int main()
{
Bar<Foo<>> a;
Bar<Baz<float>> b;
}
template <typename T>
struct Foo {
T t;
};
template <template <class> class T, class X>
struct Bar {
T<X> data;
X x;
};
int main()
{
Bar<Foo, int> a;
}
In
template <template <class X> class T>
The template type parameter X is not a template parameter to the outermost template: it is a template parameter to the innermost template. It's rather similar to
int foo(int (*bar)(int x))
{
int y = x; // compiler error
}
which doesn't work since the function takes a single argument, bar: there is no argument x.
Depending upon what you are truly trying to do, you could add the second template parameter, with something like
template <typename X, template <typename> class T >
struct Bar
{
// ...
};
you can keep the declaration with a single type parameter, but pattern match to give a partial specialization that would define the class in the example context
template <typename T>
struct Bar;
template <typename X, template <typename> class T >
struct Bar<T<X>>
{
// ...
};
you could modify Foo to have a useful nested type, and grab it that way
template <typename T>
struct Bar
{
using X = T::value_type;
};
or you could define a metafunction that extracts a template parameter from a template type, and get it that way:
template <typename T>
struct Bar
{
using X = get_parameter<T>;
};
The most flexible is the last version, except rather than a metafunction that extracts template arguments, you would declare a get_bar_parameter function, and define a partial specialization that extracts the template parameter from a Foo<X> (or a T<X>). That way, if you ever decide in the future to use Bar with classes where the right value of X isn't computed that way, you can do so by giving an appropriate specialization for get_bar_parameter.
// method 1
template <typename T>
struct Foo {
typedef T Type;
T t;
};
template <typename T>
struct Bar {
T data;
typename T::Type x;
};
// method 2
template <typename T>
struct Hack
{
T t;
};
template <typename T>
struct TypeOf
{
typedef struct UnknownType Type;
};
template<>
struct TypeOf< Hack<int> >
{
typedef int Type;
};
template <typename T>
struct Baz {
T data;
typename TypeOf<T>::Type X;
};
int main()
{
Bar< Foo<int> > a;
Baz< Hack<int> > b;
return 0;
}
In method 1 information is provided with nested type. This requires changing original class.
In method 2 this information is provided with specialization of another template.

Template specialization with a templatized type

I want to specialize a class template with the following function:
template <typename T>
class Foo
{
public:
static int bar();
};
The function has no arguments and shall return a result based on the type of Foo. (In this toy example, we return the number of bytes of the type, but in the actual application we want to return some meta-data object.)
The specialization works for fully specified types:
// specialization 1: works
template <>
int Foo<int>::bar() { return 4; }
// specialization 2: works
template <>
int Foo<double>::bar() { return 8; }
// specialization 3: works
typedef pair<int, int> IntPair;
template <>
int Foo<IntPair>::bar() { return 2 * Foo<int>::bar(); }
However, I would like to generalize this to types that depend on (other) template parameters themselves.
Adding the following specialization gives a compile-time error (VS2005):
// specialization 4: ERROR!
template <>
template <typename U, typename V>
int Foo<std::pair<U, V> >::bar() { return Foo<U>::bar() + Foo<V>::bar(); }
I am assuming this is not legal C++, but why? And is there a way to implement this type of pattern elegantly?
Partitial specialization is valid only for classes, not functions.
Workaround:
template <typename U, typename V>
class Foo<std::pair<U, V> > {
public:
static int bar() { return Foo<U>::bar() + Foo<V>::bar(); }
};
If you does not want to specialize class fully, use auxiliary struct
template<class T>
struct aux {
static int bar();
};
template <>int aux <int>::bar() { return 4; }
template <>int aux <double>::bar() { return 8; }
template <typename U, typename V>
struct aux <std::pair<U, V> > {
static int bar() { return Foo<U>::bar() + Foo<V>::bar(); }
};
template<class T>
class Foo : aux<T> {
// ...
};
It is perfectly legal in C++, it's Partial Template Specialization.
Remove the template <> and if it doesn't already exists add the explicit class template specialization and it should compile on VS2005 (but not in VC6)
// explicit class template specialization
template <typename U, typename V>
class Foo<std::pair<U, V> >
{
public:
static int bar();
};
template <typename U, typename V>
int Foo<std::pair<U, V> >::bar() { return Foo<U>::bar() + Foo<V>::bar(); }