std::enable_if specialisation failing - c++

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.

Related

Class template specialization with template template parameters

I am attempting to specialize a class on a type s.t. it ignores the constness of the given type. In this case, the type is a template template parameter:
template <class T, typename Enable = void>
struct bar
{
bar()
{
static_assert(!std::is_same<T,T>::value, "no implementation of bar for type T");
}
};
template <template <typename> class FooType, class T>
struct bar<FooType<T>, typename std::enable_if<std::is_same<typename std::remove_cv<FooType<T>>::type, foo<T>>::value>::type>
{};
The above code complains in both GCC 4.8.4 and clang 5.0 (with -std=c++11) that bar is undefined when used with a class matching the template parameterization of FooType. Even if I remove the sfinae parameter, the specialization is still unable to be found.
An example of this issue can be found here: https://godbolt.org/g/Cjci9C.
In the above example, the specialization's constructor has a static assert that goes unfound when used with a const FooType, even when the sfinae parameter is hard-coded to void. When used with a non-const FooType all works as expected.
Can someone please provide an explanation as to why the constness prohibits type deduction (matching?) in this context.
EDIT (Updated Code):
Here is a fully compile-able snippet. I tried to capture the minimum example in this snippet. The original link has been updated to reflect this example.
#include <assert.h>
#include <type_traits>
template <class T>
struct foo {};
template <class T, typename Enable = void>
struct bar
{
bar()
{
static_assert(!std::is_same<T,T>::value, "no implementation of bar for type T");
}
};
template <template <typename> class FooType, class T>
struct bar<FooType<T>, typename std::enable_if<std::is_same<typename std::remove_cv<FooType<T>>::type, foo<T>>::value>::type>
{};
int main()
{
bar<foo<int>> mut_foo; // This is fine.
// bar<const foo<int>> const_foo; // Error. No implementation found.
}
Removing the comment on the 2nd line of main triggers the static assert. I have also tried std::decay, and std::remove_const with no success.
EDIT (Non-duplicate Justification):
While the linked problem does ask a similar problem, it does not require the use of template template parameters. It also only provides a technique for solving the problem and does not justify why the given code snippet does not work. Interestingly that technique does not seem to work with template template parameters, since substituting the following snippet into the above example results in the same error:
template <template <typename> class FooType, class T>
struct bar<FooType<T>,
typename std::enable_if<std::is_same<FooType<T>, foo<T>>::value || std::is_same<FooType<T>, const foo<T>>::value>::type>
{};
const foo<int> doesn't match FooType<T>,
it would match const FooType<T> or T (or const T).
After specialization match, you might add SFINAE:
So, in your case, you may do
template <typename T> struct is_foo : std::false_type {};
template <typename T> struct is_foo<foo<T>> : std::true_type {};
template <class T>
struct bar<T,
typename std::enable_if<is_foo<typename std::remove_cv<T>::type>::value>::type>
{};

Simple concept check

Say I have a simple template like this:
template<typename T>
class A {};
And I want to specify that the type-parameter T is of some unrelated type X<U> where U is not known (or unspecifyable).
Is there a way how to express that as a concept?
Is there a way how to express that as a concept?
You don't need a concept, class template specialization works just fine in your case.
As an example, you can do this:
template<typename T>
class A;
template<typename U>
class A<X<U>> { /* ... */ };
This way, unless A is instantiated with a type of the form X<U> (where U is unknown), you'll get a compile-time error because the primary template isn't defined. In other terms, it won't work for all the types but X<U> (for each U), where the latter matches the class template specialization that has a proper definition.
Note that I assumed X is a known type. That's not clear from your question.
Anyway, if it's not and you want to accept types of the form X<U> for each X and each U, you can still do this:
template<typename T>
class A;
template<template<typename> class X, typename U>
class A<X<U>> { /* ... */ };
As a minimal, working example:
template<typename>
struct S {};
template<typename>
class A;
template<typename U>
class A<S<U>> {};
int main() {
A<S<int>> aSInt;
A<S<double>> aSDouble;
// A<char> aChar;
}
Both A<S<int>> and A<S<double>> are fine and the example compiles. If you toggle the comment, it won't compile anymore for A<char> isn't defined at all.
As a side note, if you don't want to use class template specialization and you want to simulate concepts (remember that they are not part of the standard yet and they won't be at least until 2020), you can do something like this:
#include<type_traits>
template<typename>
struct X {};
template<typename>
struct is_xu: std::false_type {};
template<typename U>
struct is_xu<X<U>>: std::true_type {};
template<typename T>
struct A {
static_assert(is_xu<T>::value, "!");
// ...
};
int main() {
A<X<int>> aXInt;
A<X<double>> aXDouble;
// A<char> aChar;
}
That is, given a generic type T, static assert its actual type by means of another structure (is_xu in the example) that verifies if T is of the form X<U> (for each U) or not.
My two cents: the class template specialization is easier to read and understand at a glance.
template <typename T, template <typename> class C>
concept bool Template = requires (T t) { {t} -> C<auto>; };
Now given a class template:
template <typename T>
struct X {};
a type template parameter can be constrained using:
template <typename T> requires Template<T, X>
class A {};
or:
template <Template<X> T>
class A {};
DEMO
This will work also for types derived from X<U>.

template specialization with default parameters

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

Partial default specialization of multiple parameter template

Is there a way to extract a partial default specialization from the compiler?
Say that I have this two parameter template:
template<typename A, typename B>
struct X {
A a;
B b;
};
and I also have some code that makes use of a single parameter template, like this:
template<template<typename> class T, typename B>
struct make_T_of_B {
T<B> member;
};
I'd like to be able to say:
make_T_of_B<X<int>, double> dummy;
where X<int> is taken as a single parameter template. It would be equivalent to this template:
template<typename B>
struct Y {
int a;
B b;
};
which looks like how one would specialize X<int, B> without actually changing anything. It's in a way similar to a default specialization -- except that a default specialization doesn't produce another template but rather an actual type (in other words, it's always total).
I realize that I can cascade the template arguments
template<typename A>
struct Z1 {
// start from scratch
template<typename B>
struct Z2 {
A a;
B b;
};
// inherit from double template above
template<typename B>
struct X: ::X<A, B> {};
};
make_T_of_B<Z1<int>::Z2, double> dummy1;
make_T_of_B<Z1<int>::X, double> dummy2;
but I find that to be rather hard to read and not communicate my intentions clearly.
Thank you.
I misunderstood your question. All you want is a way to bind the first template parameter, which you can do easily like this:
template <typename T> using Foo = X<int, T>;
Now Foo<double> is the same as X<int, double>.
Without C++11-style aliases, you can achieve the same with a bit more boilerplate:
template <typename T> struct Foo
{
typedef X<int, T> type;
};
Now you use Foo<double>::type.
I'd use a trait:
template <typename> struct applicator;
template <template <typename> class Tmpl, typename T>
struct applicator<Tmpl<T>>
{
template <typename A>
using rebind = make_T_of_B<Tmpl, A>;
};
Now you can say:
applicator<X<int>>::rebind<double> dummy;
You can of course also move the second argument, A, into the main template:
template <typename, typename> bpplicator;
template <template <typename> class Tmpl, typename T, typename A>
struct bpplicator<Tmpl<T>, A>
{
using type = make_T_of_B<Tmpl, A>; // or "typedef make_T_of_B<Tmpl, A> type;"
};
bpplicator<X<int>, double>::type dummy;
This has the advantage that it works in C++03, too.

Getting at template parameter types of a template type

I have a question about templates and it is in the code:
template<typename T>
struct foo {
T t;
};
template<typename FooType>
struct bar {
T t; //<- how to get T here (preferably without using typedef in foo)
};
Here's a generic template argument type extractor:
#include <tuple>
template <typename> struct tuplify;
template <template <typename...> class Tpl, typename ...Args>
struct tuplify<Tpl<Args...>>
{
using type = std::tuple<Args...>;
};
template <typename T, unsigned int N>
using get_template_argument
= typename std::tuple_element<N, typename tuplify<T>::type>::type;
Usage:
get_template_argument<std::vector<int>, 1> a; // is a std::allocator<int>
Or in your case:
get_template_argument<FooType, 0> t;
If I understood your question correctly, you could use template specialization as follows. Given your foo<> class template:
template<typename T>
struct foo {
T t;
};
Define a bar<> primary template and a corresponding specialization this way:
template<typename FooType>
struct bar;
template<typename T>
struct bar<foo<T>> {
T t; // T will be int if the template argument is foo<int>
};
Under the assumption that you are always supposed to instantiate bar by providing an instance of foo<> as the type argument, you can leave the primary template undefined.
The specialization will match the foo<T> pattern, thus giving you the type with which foo<> is instantiated in T.
Here is how you could test the validity of this approach with a simple program:
#include <type_traits>
int main()
{
bar<foo<int>> b;
// This will not fire, proving T was correctly deduced to be int
static_assert(std::is_same<decltype(b.t), int>::value, "!");
}
Here is the corresponding live example.
If you don't want or can't add a typedef to foo, you can additionally write an independent "extractor" template
template <typename T> struct ExtractT;
template <typename T> struct ExtractT<foo<T> > {
typedef T type;
};
and use it as
template<typename FooType>
struct bar {
typename ExtractT<FooType>::type t;
};
You can take that ExtractT one step further and decouple it from foo
template <typename T> struct ExtractT;
template <template <typename> class C, typename T> struct ExtractT<C<T> > {
typedef T type;
};
and so on until you reinvent something from Boost or C++11 standard library :) BTW, this feels like something that should already be available in form of a more generic solution....