ambiguous template weirdness - c++

I have the following code (sorry for the large code chunk, but I could not narrow it down any more)
template <bool B>
struct enable_if_c {
typedef void type;
};
template <>
struct enable_if_c<false> {};
template <class Cond>
struct enable_if : public enable_if_c<Cond::value> {};
template <typename X>
struct Base { enum { value = 1 }; };
template <typename X, typename Y=Base<X>, typename Z=void>
struct Foo;
template <typename X>
struct Foo<X, Base<X>, void> { enum { value = 0 }; };
template <typename X, typename Y>
struct Foo<X, Y, typename enable_if<Y>::type > { enum { value = 1 }; };
int main(int, char**) {
Foo<int> foo;
}
But it fails to compile with gcc (v4.3) with
foo.cc: In function ‘int main(int, char**)’:
foo.cc:33: error: ambiguous class template instantiation for ‘struct Foo<int, Base<int>, void>’
foo.cc:24: error: candidates are: struct Foo<X, Base<X>, void>
foo.cc:27: error: struct Foo<X, Y, typename enable_if<Y>::type>
foo.cc:33: error: aggregate ‘Foo<int, Base<int>, void> foo’ has incomplete type and cannot be defined
OK, so it's ambiguous. but I wasn't expecting it to be a problem as when using specialization it will almost always be some ambiguity. However this error is only triggered when using the class with enable_if<...>, if I replace it with a class like the following there is no problem.
template <typename X, typename Y>
struct Foo<X, Y, void > { enum { value = 2 }; };
Why does this class not cause an ambiguity while the others do? Isn't the two the same thing for classes with a true ::value?
Anyway, any hints as to what I am doing wrong are appreciated.
Thanks for the answers, my real problem (to get the compiler to select my first specialization) was solved by replacing struct Foo<X, Base<X>, void> with struct Foo<X, Base<X>, typename enable_if< Base<X> >::type > which seems to work the way I want.

The gist of your question is that you have:
template <typename X, typename Y, typename Z>
struct Foo {};
template <typename X>
struct Foo<X, Base<X>, void> {}; // #1
template <typename X, typename Y>
struct Foo<X, Y, typename whatever<Y>::type> {}; // #2
and you're trying to match it to
Foo<int, Base<int>, void>
Obviously, both specializations match (the first with X = int, the second with X = int, Y = Base<int>).
According to the standard, section 14.5.4, if there are more matching specializations, a partial ordering (as defined in 14.5.5.2) among them is constructed and the most specialized one is used. In your case, however, neither one is more specialized than the other. (Simply put, a template is more specialized than another, if you can replace each type parameter of the latter template with some type and in result get the signature of the former. Also, if you have whatever<Y>::type and you replace Y with Base<X> you get whatever<Base<X> >::type not void, i.e. there is not processing performed.)
If you replace #2 with
template <typename X, typename Y>
struct Foo<X, Y, void > {}; // #3
then the candidate set again contains both templates, however, #1 is more specialized then #3 and as such is selected.

aren't you missing a
<
symbol ?

I think you are missing a '<', a template should look like:
template< typename T >
struct myStruct
{};
//OR
template< class T >
struct myStruct
{};

Related

How the when<> trait in boost.Hana works?

I have some experience with std::enable_if. IIRC, it is about if a well-formed expression results in true return back the user type T (if given) or void via nested type alias.
template<bool,typename = void>
struct enable_if;
template<typename T>
struct enable_if<true,T>{
using type = T;
};
template <typename T, typename = void>
struct base_template{
enum { value= false};
};
template <typename T>
struct base_template<T, typename enable_if<std::is_integral<T>::value>::type> {
enum { value= true};// something useful...
};
struct some{};
static_assert(base_template<some>::value,"F*"); //error: static assertion failed: F
static_assert(base_template<int>::value,"F*");
But in boost.Hana I see this trait when<> and its implementation is like
template <bool condition>
struct when;
template <typename T, typename = when<true>>
struct base_template{
enum { value= false};
};
template <typename T>
struct base_template<T, when<std::is_integral<T>::value>> {
enum { value= true};// something useful...
};
struct some{};
static_assert(base_template<int>::value,"F*");
static_assert(base_template<some>::value,"F*");<source>:28:15: error: static assertion failed: F*
How the SFINAE works here? though the std::is_integral<some>::value is going to result in false, it doesn't mean(it does?) that the when<false> is ill-formed and should dispatch the instantiation to the primary class template. Am I missing anything here?
It's the same general idea. You can use enable_if_t or decltype in basically the same way. Now, you're probably used to seeing SFINAE partial specializations like this:
template<class T, class U = void>
struct Foo {};
template<class T>
struct Foo<T, decltype(T::bar())> {};
... Foo<X> ...
Here, Foo<X> is first expanded by the compiler to Foo<X, void> (because you didn't provide U at the "call site", so the default U = void is filled in instead). Then, the compiler looks for the best-matching specialization of class template Foo. If decltype(X::bar()) is in fact void, then Foo<T, decltype(T::bar())> [with T=X] will be a perfect match for Foo<X, void>. Otherwise, the generic Foo<T, U> [with T=X, U=void] will be used instead.
Now for the Hana when example.
template<bool> struct when {};
template<class T, class U = when<true>>
struct Foo {};
template<class T>
struct Foo<T, when<T::baz>> {};
... Foo<X> ...
Here, Foo<X> is first expanded by the compiler to Foo<X, when<true>> (because you didn't provide U at the "call site", so the default U = when<true> is filled in instead). Then, the compiler looks for the best-matching specialization of class template Foo. If when<X::baz> is in fact when<true>, then Foo<T, when<T::baz>> [with T=X] will be a perfect match for Foo<X, when<true>>. Otherwise, the generic Foo<T, U> [with T=X, U=when<true>] will be used instead.
You can replace the simple expression T::baz in my example with any arbitrarily complicated boolean expression, as long as it's constexpr-evaluable. In your original example, the expression was std::is_integral<T>::value.
My CppCon 2017 session "A Soupçon of SFINAE" walks through some similar examples.

Using `std::conditional_t` to define a class' `typedef` in dependence of its template parameter

I try to use std::conditional_t to define a class A's typedef in dependence of its template parameter T:
template< typename T >
class A
{
public:
typedef std::conditional_t< std::is_fundamental<T>::value, T, decltype(std::declval<T>().foo())> type;
};
template< typename T >
class B
{
public:
T foo()
{
// ...
}
};
int main()
{
typename A< int >::type a = 5; // causes an error
typename A< B<int> >::type b = 5; // does not cause an error
return 0;
}
Unfortunately, the code does not compile. Error:
error: member reference base type 'int' is not a structure or union
typedef std::conditional_t< std::is_fundamental<T>::value, T, decltype(std::declval<T>().foo())> type;
Does anyone know how to fix it?
Both types in the conditional expression must be valid, this isn't a SFINAE context.
You can achieve what you want by "executing" only the chosen class template:
typedef typename std::conditional_t< std::is_fundamental<T>::value, identity<T>, foo_t<T>>::type type;
with those defined as:
template<typename T>
struct identity{ using type = T; };
template<typename T>
struct foo_t{ using type = decltype(std::declval<T>().foo()); };
demo
The compiler need to instantiate the whole thing when you ask for ::type. With ints, it's like asking the compiler to give you that type:
std::conditional_t<true, int, decltype(std::declval<int>().foo())>
But unfortunately, this is ill formed. You'll need to ask the compiler to do some sfinae to choose the right type:
template<typename T, typename = void>
struct A {
using type = T;
};
template<typename T>
struct A<T, void_t<decltype(std::declval<T>().foo())>> {
using type = decltype(std::declval<T>().foo());
};
The compiler will choose the second specialization if possible. In other words, if the type T really has .foo() possible, then type will be equal to the type of that expression.
You can implement void_t like that:
template<typename...>
using void_t = void;
The advantage of this silution is that it don't work only with fundamentals, but the compiler will choose the first version if it can't find a .foo() function in the type. You could even add a third version:
template<typename T>
struct A<T, void_t<decltype(std::declval<T>().bar())>> {
using type = decltype(std::declval<T>().bar());
};
Now your struct works with types that have .bar() too.
template<template<class...>class Z>
struct wrap_z{
template<class...Ts>
using result=Z<Ts...>;
};
template<bool b, template<class...>class T, template<class...>class F, class...Ts>
using conditional_apply =
std::conditional_t<
b, wrap_z<T>, wrap_z<F>
>::template result<Ts...>;
template<class T>using identity = T;
now we use it:
public:
template<class T>using do_foo = decltype(std::declval<T>().foo());
using type = conditional_apply< std::is_fundamental<T>{}, identity, do_foo, T>;
which I think is clearer at point-of-use than alternatives.

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.

Template pattern matching

Consider this class template:
template <typename T1, typename T2, bool B>
class SomeClass { };
Now, I'd like to provide two implementations based on B==true and B==false. That is, I'd like to say something like:
template <ANYTHING, ANYTHING, true> class SomeClass {
// First implementation
};
template <ANYTHING, ANYTHING, false> class SomeClass {
// Second implementation
};
How can this be done in C++(11)?
With partial specialization:
// primary
template<typename X, typename Bool>
struct Foo;
template<typename X>
struct Foo<X, std::true_type> {};
template<typename X>
struct Foo<X, std::false_type> {};
// use
Foo<X, std::true_type> x;
I use a type-wrapper for bool, but you can also do that with
non-type template parameters:
// primary
template<typename, bool>
struct Foo;
template<typename X>
struct Foo<X, true> {};
template<typename X>
struct Foo<X, false> {};
// use
Foo<X, true> x;
Sometimes you can compute the value used for partial specialization
with meta-programming in the default argument:
// primary
template<typename X, typename is_integral_ = std::is_integral<X>::type>
struct Foo;
This makes the configuration variable overridable by user choice.
struct my {};
Foo<my, std::true_type> x;
To prevent that, dispatch through inheritance:
// primary, where Foo_impl is any of the above
template<typename X>
struct Foo : public Foo_impl<X> {};
It's called partial specialization:
template <typename T1, typename T2> class SomeClass<T1 ,T2, true> {
// First implementation
};
template <typename T1, typename T2> class SomeClass<T1, T2, false> {
// Second implementation
};
As the name indicates it is related to (full) specialization which looks like this:
template <> class SomeClass<int, char, false> {
// dedicated version for T1=int, T2=char, B=false
};
Note that if most of the implementation is the same, you can also write a single generic version, where only that code which depends on the bool argument is delegated to a trait class. In this case, the trait class would be fully-specialized on a single argument.