The following code compiles. Anyone can explain why? I've been digging the standard to figure out why it's legal.
template <bool B, typename T = void> struct enable_if { };
template <typename T> struct enable_if<true, T> {
typedef T type;
};
template <typename T, typename Enable = void> struct A;
template <typename T> struct A<T, typename enable_if<(sizeof(T) <= ~0ULL)>::type> {
void f() { }
};
int main() {
A<int> a;
a.f();
}
At the statement:
A<int> a;
As there's only one template paramerter "int", the compiler should go to use the primary template, which is:
template <typename T, typename Enable = void> struct A;
which is undefined, thus causing an error.
From § 14.5.5.1
1 When a class template is used in a context that requires an
instantiation of the class, it is necessary to determine whether the
instantiation is to be generated using the primary template or one of
the partial specializations. This is done by matching the template
arguments of the class template specialization with the template
argument lists of the partial specializations.
— If exactly one matching specialization is found, the instantiation is generated from
that specialization.
Let's try to figure out what's going on here:
// definition of enable_if, second parameter is defaulted to void
template <bool B, typename T = void>
struct enable_if { };
// specialization of enable_if, if first parameter is true,
// enable_if has a typedef for the second parameter
template <typename T>
struct enable_if<true, T> {
typedef T type;
};
// definition of struct A, second parameter defaults to void
template <typename T, typename Enable = void>
struct A;
// specialization of struct A, second parameter
// is obtained from the enable_if::type typedef
// the first parameter of enable_if is true if the size of T
// is smaller than the max long long (~0 --> all F)
template <typename T>
struct A<T, typename enable_if<(sizeof(T) <= ~0ULL)>::type> {
void f() { }
};
int main() {
// So we attempt the specialization for struct A<int,enable_if...>
// The expression of enable_if evaluates to...
// (sizeof(int) <= ~0ULL) == true
// ... so it applies the specialization of enable_if<true,void>
// (second parameter is void because none is provided, so it
// uses the default.
// so the enable_if template is valid (selected the specialization)
// and that means that the struct A<int,enable_if> specialization
// is valid too, so it is selected.
A<int> a;
a.f();
}
The compiler uses the template A<int, enable_if<true>:::type > when you declare A<int> since sizeof(int) <= ~0ULL evaluates to true.
There is no problem with enable_if<true>::type because the compiler is able to use enable_if<true, true>::type.
When you consider your enable_if:
template <bool B, typename T = void> struct enable_if{ };
template <typename T> struct enable_if<true, T>
{
typedef T type;
};
in
void test_EnableIf
{
static_assert(
std::is_same<
enable_if<(sizeof(int) > 0)>::type,
void>::value, "test_EnableIf failed." );
}
the result (type) is void, as no type was specified (as second
template parameter). The specialization of enable_if is selected
because of the boolean expression being true, and the default
parameter is selected (from primary template) because no other was
provided, and hence type is void, but NOTE that the definition
of type does exist (as the specialization was selected).
Now, in your definition of A...
template <typename T, typename Enable = void> struct A;
template <typename T>
struct A<T, typename enable_if<
(sizeof(T) <= ~0ULL)>::type>
{
void f() { }
};
...because type does exist in enable_if, it is a better match, which causes the specialization to be selected, and hence compiles.
A trivial example which amounts to the same thing is the following:
template <class T, class U = void>
struct X;
template <class T>
struct X<T,void>
{
static int foo(){ return 0; }
};
int main()
{
return X<int>::foo();
}
Related
Suppose I had a templated object in C++ Test<T> and I wanted to find out what the T value is so that when I pass Test<T> as a template argument to TestWrapper<Test<T>>, I can declare a variable called T extradata in the object TestWrapper that is the same as Test's T type.
How can I achieve this by only modifying TestWrapper (i.e. making no changes to Test struct) ? Additionally is it possible to modify it so that it will extract from any object that takes 1 template parameter e.g. foo<T>, bar<T>, rather then just Test<T>>?
Thanks in advance.
template<typename T>
struct Test {
T value;
};
//template -> extract first type parameter from any object as T
struct TestWrapper {
T extradata;
};
int main() {
TestWrapper<Test<int>> temp;
temp.extradata = 123; // temp.extradata is a int, deduced from Test<int>
}
You can use partial template specialization to do that.
template<typename T>
struct Test {
T value;
};
template <typename T>
struct TestWrapper;
template <template <typename> typename Outer, typename T>
struct TestWrapper<Outer<T>> {
T extradata;
};
int main() {
TestWrapper<Test<int>> temp;
temp.extradata = 123; // temp.extradata is a int, deduced from Test<int>
}
In this example we have not defined any default definition for TestWrapper, so it will fail to compile if you give something that does not match the specialization.
You can
// primary template
template <typename T>
struct TestWrapper {
T extradata; // declare extradata with type T
// how to declare it depends on your intent
};
// partial specialization
template <template <typename...> class C, typename T, typename... Args>
struct TestWrapper<C<T, Args...>> {
T extradata; // T is the 1st template parameter of C here
};
You can create a helper class to extract the first parameter of the instantiated template, for example:
template<class>
struct Extract;
template<template<class...> class V, class First, class... Rest>
struct Extract<V<First, Rest...>> {
using type = First;
};
template<class T>
struct TestWrapper {
typename Extract<T>::type extradata;
};
Demo.
I'm trying to understand enable_if and SFINAE. So far, my use of templates has been explicitly passed template arguments to the template parameters or deduction of template parameters during instantiation. I'm confused in the code below how the member of enable_if<>::type (typedef int type) can be a template parameter. I.e. the parameter list becomes <class T = int, int* = nullptr>. I would expect a parameter like this to be int Z = nullptr.
Does anyone know what I'm missing in regards to understanding why these template parameters assigned by enable_if work?
Thanks
#include <type_traits>
#include <iostream>
#if 1
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void do_stuff(T& t) {
std::cout << "do_stuff integral\n";
}
#endif
#if 0 // understood
template <class T>
typename std::enable_if<std::is_integral<T>::value,
void>::type
do_stuff(T& t) {
std::cout << "do_stuff integral\n";
}
#endif
struct S {};
int main(int argc, char *argv[])
{
int i = 1;
do_stuff(i);
//do_stuff<S>(S{});
return 0;
}
There are two kinds of template parameters:
type template parameters
non-type template parameters
The name of the template parameter is optional
A template parameter can have a default value
Type template parameter
named, non-defaulted
template <class T>
struct X {};
Usage:
X<int> x{};
named, defaulted
template <class T = int>
struct X {};
Usage:
X<> x1{};
X<char> x2{};
unnamed non-defaulted
template <class>
struct X {};
Usage:
X<int> x{};
unnamed, defaulted
template <class = int>
struct X {};
Usage:
X<> x1{};
X<char> x2{};
Non-type template parameter
named, non-defaulted
template <int I>
struct X {};
Usage:
X<24> x{};
named, defaulted
template <int I = 24>
struct X {};
Usage:
X<> x1{};
X<11> x2{};
unnamed, non-defaulted
template <int>
struct X {};
Usage:
X<11> x{};
unnamed, defaulted
template <int = 24>
struct X {};
Usage:
X<> x1{};
X<11> x2{};
For SFINAE the technique requires a default value. Since you don't care for the parameter inside the class you can skip its name. As for the type or non-type you can chose which one is easier to write in your particular case.
The parameter is anonymous. The type and default is specified, but there's nothing to actually refer to it later.
template<typename> struct A; // fine
template<int> struct B; // fine
template<typename F, F = nullptr> struct C; // fine
using AI = A<int>;
using BI = B<5>;
using CI = C<int, 5>;
using CD = C<void*>; // all fine
using Oops = C<char>; // not fine
Your case just replaces the simpler types like int and F with a big honking typename std::enable_if<...>::type. We don't care about the actual argument that gets passed it ends up being, so that's why it's unnamed.
template <class T,
typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void do_stuff(T &t) {
std::cout << "do_stuff integral\n";
}
Plugging in T = int, typename std::enable_if<std::is_integral<T>::value>::type = void, so the second defaulted parameter just looks like void* = nullptr, which means it's an unnamed parameter of type void* that defaults to nullptr. Note that the typename is part of the type of a non-type template parameter. It is not serving in its other capacity as the introducer of type template parameters. Also, please note that the modern C++ way to write this is
// typename instead of class is more a style thing
template<typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr>
where enable_if_t and is_integral_v are aliases that abbreviate all the ::value and typename ...::type nonsense:
namespace std {
template<typename T>
constexpr inline bool is_integral_v = std::is_integral<T>::value;
template<bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
}
Similarily to this question is it possible to use SFINAE to determine if a type has a member function with a certain argument(s)? Without the argument list as answered in the other question and the std::void_t example says it works fine. (I stood with the latter.) However, If I try to add a check for argument lust with extra std::decltype<>() it fails with template parameters not deducible in partial specialization. Is there any way to extend this method to check for certain argument types?
Example code:
#include <type_traits>
class A {
public :
void a(){}
void b(int val){}
};
class B {
public :
void b(float val){}
};
// --- Has function a() without arguments
template <typename T, typename = void> struct has_a : std::false_type
{
};
template <typename T> struct has_a<T, std::void_t<decltype(std::declval<T>().a())>> : std::true_type
{
};
template <typename T> constexpr bool has_a_v = has_a<T>::value;
// This is OK:
static_assert(true == has_a_v<A>);
static_assert(false == has_a_v<B>);
// --- Has function b() with one argument of one given type
template <typename T, typename U, typename = void> struct has_b : std::false_type
{
};
template <typename T ,typename U> struct has_b<T, std::void_t<decltype(std::declval<T>().b(std::declval<U>()))>> : std::true_type
{
};
template <typename T, typename U> constexpr bool has_b_v = has_b<T, U>::value;
// This fails with `template parameters not deducible in partial specialization`:
static_assert(true == has_b_v<A, int>);
static_assert(false == has_b_v<B, int>);
static_assert(false == has_b_v<A, float>);
static_assert(true == has_b_v<B, float>);
int main () { return 0;}
Yes. Example of a void_t check for member function b in class B:
decltype( static_cast<void(B::*)(float)>(&B::b) )
This is if you want to check for exact signature. Your way is fine, too (once you fix it as commented under the question), but it actually checks if member function is callable with certain types of arguments (and ignores the return type).
In your partial specialization, you forgot to add the U parameter:
template <typename T, typename U>
struct has_b<
T,
U, // You forgot it
std::void_t<decltype(std::declval<T>().b(std::declval<U>()))>> : std::true_type
{
};
According to the reference, the name of a non-type template parameter is optional, even when assigning a default value (see (1) and (2)). Therefore these template structs are valid:
template <int> struct Foo {};
template <unsigned long = 42> struct Bar {};
I haven't seen a possibility of accessing the values of the non-type parameters.
My question is: What's the point of unnamed/anonymous non-type template parameters? Why are the names optional?
First, we can split declaration from definition.
So name in declaration is not really helpful. and name might be used in definition
template <int> struct Foo;
template <unsigned long = 42> struct Bar;
template <int N> struct Foo {/*..*/};
template <unsigned long N> struct Bar {/*..*/};
Specialization is a special case of definition.
Then name can be unused, so we might omit it:
template <std::size_t, typename T>
using always_t = T;
template <std::size_t ... Is, typename T>
struct MyArray<std::index_sequence<Is...>, T>
{
MyArray(always_t<Is, const T&>... v) : /*..*/
};
or used for SFINAE
template <typename T, std::size_t = T::size()>
struct some_sized_type;
What's the point of unnamed/anonymous non-type template parameters?
I can think of specialization:
template<int = 42>
struct Foo{
char x;
};
template<>
struct Foo<0> {
int x;
};
template<>
struct Foo<1> {
long x;
};
Then:
Foo<0> a; // x data member is int
Foo<1> b; // x data member is long
Foo<7> c; // x data member is char
Foo<> d; // x data member is char
Oh, you can access them!
template <int> struct Foo {};
template <int N>
int get(Foo<N>) {
return N;
}
int main() {
Foo<3> foo;
return get(foo);
}
This might be a bit contrived. But in general for some templates you don't want to name them and then it is convenient that you don't have to.
Unamed type and non-type parameters also allow you to delay type instanciation, using template-template parameters.
Take destination_type in the function below for instance.
It can resolve to any type that has a template-type parameter, and 0 to N template-values parameters.
template <template <typename, auto...> typename destination_type, typename TupleType>
constexpr auto repack(TupleType && tuple_value)
{
return [&tuple_value]<std::size_t ... indexes>(std::index_sequence<indexes...>) {
return destination_type{std::get<indexes>(tuple_value)...};
}(std::make_index_sequence<std::tuple_size_v<TupleType>>{});
}
static_assert(repack<std::array>(std::tuple{1,2,3}) == std::array{1,2,3});
Such mechanic comes handy when you need an abstraction on parameters-pack.
Here, for instance, we do not care if Ts... is a parameter-pack containing multiple arguments, or expand to a single type which itself has multiples template parameters.
-> Which can be transposed from template-type parameters to template-value parameters.
See gcl::mp::type_traits::pack_arguments_as_t
Complete example available on godbolt here.
template <template <typename ...> class T, typename ... Ts>
class pack_arguments_as {
template <template <typename...> class PackType, typename... PackArgs>
constexpr static auto impl(PackType<PackArgs...>)
{
return T<PackArgs...>{};
}
template <typename... PackArgs>
constexpr static auto impl(PackArgs...)
{
return T<PackArgs...>{};
}
public:
using type = decltype(impl(std::declval<Ts>()...));
};
template <template <typename ...> class T, typename ... Ts>
using pack_arguments_as_t = typename pack_arguments_as<T, Ts...>::type;
namespace tests
{
template <typename... Ts>
struct pack_type {};
using toto = pack_arguments_as_t<std::tuple, pack_type<int, double, float>>;
using titi = pack_arguments_as_t<std::tuple, int, double, float>;
static_assert(std::is_same_v<toto, titi>);
static_assert(std::is_same_v<toto, std::tuple<int, double, float>>);
static_assert(std::is_same_v<pack_type<int, double, float>, pack_arguments_as_t<pack_type, toto>>);
}
I've been messing around with enable_if, and I seem to have stumbled upon some inconsistent behaviour. This is in VS2010. I've reduced it to the following sample.
#include <type_traits>
using namespace std;
// enable_if in initial template definition
template <class T, class Enable = enable_if<true>> struct foo {};
foo<int> a; //OK
// explicit specialisation
template <class T, class Enable = void> struct bar;
template <class T> struct bar<T, void> {};
bar<int> b; //OK
// enable_if based specialisation
template <class T, class Enable = void> struct baz;
template <class T> struct baz<T, std::enable_if<true>> {};
baz<int> c; //error C2079: 'c' uses undefined struct 'baz<T>'
Is this a bug in the code or the compiler?
std::enable_if<true> should be typename std::enable_if<true>::type.
std::enable_if<true> always names a type (as does std::enable_if<false>). In order to get substitution to fail when the condition is false you need to use the type nested typedef, which is only defined if the condition is true.
Your problem has very little to do with enable_if
// you declare a structure named baz which takes 2 template parameters, with void
// as the default value of the second one.
template <class T, class Enable = void> struct baz;
// Partial specialization for a baz with T and an std::enable_if<true>
template <class T> struct baz<T, std::enable_if<true>> {};
// Declare a baz<int, void> (remember the default parameter?):
baz<int> c; //error C2079: 'c' uses undefined struct 'baz<T>'
baz<int, void> has an incomplete type at that point. The same problem will occur without enable_if:
template <class T, class U = void>
struct S;
template <class T>
struct S<T, int>
{ };
S<double> s;
And, as James said, you're using enable_if incorrectly. Boost's documentation for enable_if does a great job explaining it.