again about C++ SFINAE in enable_if_t - c++

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.

Related

How to tell if template type is an instance of a non-variadic template class?

This question is awful similar to How to tell if template type is an instance of a template class?
I would like to detect if a template parameter is from one particular template class that has no variadic template arguments.
template<class U, class S>
struct A{};
template<class T>
struct B {
B() {
if constexpr (T == A) {
// T is a template instantiation of `A`.
} else {
}
}
};
I can't change A's definition. I can change B's definition to have additional template parameters.
How do I implement (T == A) given the restriction of not knowing A's U and S?
I would go for a partial specialization here.
#include <iostream>
template<class U, class S>
struct A{};
template<class T>
struct B {
B() {
std::cout << "None-A implementation\n";
}
};
template<class U, class S>
struct B<A<U, S>> {
B() {
std::cout << "A implementation\n";
}
};
int main() {
B<int> b1;
B<A<int, int>> b2;
}
You have the option of leaving the default-case without an implementation.
Or you can have a fallback implementation for any none-A classes like here.
If the partial specialization forces too much code duplication you can also extract the detection part to it's own template variable like this.
#include <iostream>
template<class U, class S>
struct A{};
template <class T>
constexpr bool is_A_instatiation = false;
template <class U, class S>
constexpr bool is_A_instatiation<A<U, S>> = true;
template<class T>
struct B {
B() {
if constexpr (is_A_instatiation<T>) {
std::cout << "A instatiation!\n";
} else {
std::cout << "none-A instatiation!\n";
}
}
};
int main() {
B<int> b1;
B<A<int, int>> b2;
}
The easiest way is:
template<class T>
struct B{/*default implementation*/};
template<class U,class S>
struct B<A<U,S>>{/*Specified implementation*/};
A<T,U>: you already know it and search key
B<...>: variadic types which may include A<T,U> - known type
And you want to search A<T,U> in B<...>
template <typename T, typename U>
struct A {};
template <typename T, typename U, typename ...Ts>
struct B {
static constexpr bool value = ((std::is_same_v< A<T, U>, Ts> || ... ));
};
int main() {
std::cout << std::boolalpha <<
B<int,float, int, int, float, A<int,float>>::value << '\n'<<
B<int,float, int, int, float>::value <<std::endl;
}

static template variable in template class

I want to define the static template variable of a templated class. But I can't get the correct syntax here:
template < typename T>
class X
{
public:
T i;
X(T _i): i{_i}{}
operator T(){ return i; }
};
template < typename T2 >
class Y
{
public:
template <typename T>
static X<T> x_in_y;
};
// something like that, which currently do not compile
template< typename T2, typename T>
X<T> Y<T2>::x_in_y<T>{9.9};
int main()
{
std::cout << Y<int>::x_in_y<float> << std::endl;
}
x_in_y is a template in a template so you need a nested template declaration:
template<typename T2>
template<typename T>
X<T> Y<T2>::x_in_y{9.9};

Passing specified template type as template parameter

Say I have some template type...
template <typename T> struct Foo {
Foo(T t) {}
};
Is there a way to pass a specified Foo type to a function so that the function has direct visibility of T?
Ideally I would be able to write something like this...
Foo<int> foo = create<Foo<int>>();
The closest I've been able to come is
template <
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
which would then be used like
Foo<int> foo = create<Foo, int>();
Thanks for any help.
This form of template template parameter is only allowed in C++17:
template < // v---------- typename here not allowed
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
You must replace the typename pointed out by class:
template < // v---------- class allowed
template <typename> class TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
In C++17, both compiles and are equivalent.
To make your syntax Foo<int> foo = create<Foo<int>>(); work, you simply need to do this:
template <typename T>
T create() {
return T{};
}
If you want to limit what type can be sent, you must create a type trait:
// default case has no typedef
template<typename>
struct first_param {};
// when a template is sent, define the typedef `type` to be equal to T
template<template<typename> class TT, typename T>
struct first_param<TT<T>> {
using type = T;
};
// template alias to omit `typename` everywhere we want to use the trait.
template<typename T>
using first_param_t = typename first_param<T>::type;
Then, use your trait:
template <
typename T,
void_t<first_param_t<T>>* = nullptr
> // ^---- if the typedef is not defined, it's a subtitution error.
T create() {
return T(first_param_t<T>{});
}
You can implement void_t like this:
template<typename...>
using void_t = void;
Live at Coliru
One simple way is to add the sub-type information in Foo directly:
template <typename T> struct Foo {
using type = T;
Foo(T t) {}
};
and then
template <typename FooT>
FooT create() {
return FooT(typename FooT::type{});
}
You might add SFINAE if you want:
template <typename FooT>
auto create()
-> decltype(FooT(typename FooT::type{}))
{
return FooT(typename FooT::type{});
}
If you want really restrict the function to Foo exclusively, you have to create a traits and SFINAE on it.
Why not simply use a tag dispatching, e.g.:
template <class>
struct tag { };
template <class T>
Foo<T> create(tag<Foo<T>>) {
return Foo<T>(T());
}
//...
Foo<int> foo = create(tag<Foo<int>>{});
In C++11
Demo
The gist is to have an entry point function named create that can instantiate a create_helper struct to create the proper type.
We can create our structures using template specialization so that we're forcing a templated class to be passed.
Full code:
template<class T>
struct create_helper
{
static_assert(sizeof(T) == 0, "Need to pass templated type to create");
};
template <class T, template<class> class TT>
struct create_helper<TT<T>>
{
static TT<T> apply()
{
return {T{}};
}
};
template<class T>
auto create() -> decltype(create_helper<T>::apply())
{
return create_helper<T>::apply();
}
And a test:
template<class T>
struct Foo
{
Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;}
};
int main()
{
Foo<int> foo = create<Foo<int>>();
}
Output:
Constructed Foo with value 0

Access type member

In my example I have a class Foo<T>. In my function test I need to get the template parameter of Foo otherwise the normal type. First I started to use std::conditional but forgot that the template parameters must all be valid, no matter which one is picked. Is the only way to create a type-specialisation for non-Foo types?
Example
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const T& a)
{
// actually I would have used !is_foo<T>::value for the first arg
// but this check is fine to minimise the example
using MY_TYPE = typename std::conditional<
std::is_same<T, int>::value,
T,
typename T::M>::type; // <---Error: error: type 'int' cannot be used prior to '::' because it has no members
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
Well you could make an UnFoo helper to get the right type for you:
template <typename T>
struct UnFoo {
using type = T;
};
template <typename T>
struct UnFoo<Foo<T>> {
using type = T;
};
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename UnFoo<T>::type; //maybe with a helper to get rid of typename
}
Another option would be to write an overload for Foo<T> and have it delegate to the other function, but that depends on what your real test function does.
You can do some void_t magic to allow SFINAE to figure help you out:
#include <type_traits>
#include <iostream>
#include <typeinfo>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
// primary template handles types that have no nested ::T member:
template< class T, class = void_t<> >
struct M_or_T { using type = T; };
// specialization recognizes types that do have a nested ::T member:
template< class T >
struct M_or_T<T, void_t<typename T::M>> { using type = typename T::M; };
template <typename T>
void test(const T& a)
{
using MY_TYPE = typename M_or_T<T>::type;
std::cout << typeid(MY_TYPE).name() << "\n";
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
What happens is that the second overload of M_or_T substitution fails for int (and for any type without a type member M) and thus the first overload is chosen. For types which have a type member M, a more specialized second overload is chosen.
#include <type_traits>
template <typename TYPE>
class Foo
{
public:
using M = TYPE;
};
template <typename T>
void test(const Foo<T>& a)
{
using MY_TYPE = Foo<T>::M;
testOther<MY_TYPE>(a);
}
template <typename T>
void test(const T& a)
{
using MY_TYPE = T;
testOther<MY_TYPE>(a);
}
template <typename T, typename S>
void testOther(const S& a)
{
// do stuff
}
int main()
{
test(Foo<int>()); // MY_TYPE must be int
test(int()); // MY_TYPE must be int
return 0;
}
I'm not exactly sure what you wanted, but I hope this is what you wanted. It might be a bit off. I didn't compile this.

Why SFINAE requires the 'Enable' class template parameter?

(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();
}