Bizarre behavior involving enable_if and partial specializations

T

trbabb

I've come across a bizarre case where the member type of enable_if seems tocontrol whether a partial specialization is found. If I pass any type other than the default of `void`, the specialization is hidden. I would not expect this type to matter at all, since in this case its only purpose is to trigger or disable SFINAE; the template parameter it fills is otherwise unused.

Consider this fairly simple case:

//============ specialize.cpp ============//

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <iostream>

using namespace boost;
using namespace std;

/***************************
* specialized type = void *
***************************/

template <typename T, typename Enable=void>
struct IsSpecialized {
static const bool specialized = false;
};

template <typename T>
struct IsSpecialized <T, typename enable_if<is_integral<T> >::type> {
static const bool specialized = true;
};

/***************************
* specialized type = int *
***************************/

template <typename T, typename Enable=void>
struct IsSpecializedInt {
static const bool specialized = false;
};

// note the `int` below:
template <typename T>
struct IsSpecializedInt <T, typename enable_if<is_integral<T>, int>::type> {
static const bool specialized = true;
};

/***************************
* main *
***************************/

int main(int argc, char **argv) {
cout << "specialized? (enable_if<...>::type = void): ";
cout << IsSpecialized<int>::specialized << endl;

cout << "specialized? (enable_if<...>::type = int): ";
cout << IsSpecializedInt<int>::specialized << endl;

return 0;
}

//============ end specialize.cpp ============//

The output:
specialized? (enable_if<...>::type = void): 1
specialized? (enable_if<...>::type = int): 0

It does not seem to matter whether I pass `int` or any other type to enable_if; only `void` seems to give the expected behavior.

The example above is scarcely modified from the example in the boost library documentation on enable_if for partial specializations.

I am using gcc, and this manifests with every release I have tried (including the current 4.8.1).

I cannot fathom a ruleset in which this makes sense. Is behavior in keepingwith the standard? If so, why does this happen? What is magical about `void` in the context of template resolution?

-tb
 
Ö

Öö Tiib

using namespace boost;
using namespace std;

Writing that may get your contract terminated immediately. Do you
realize how lot of common names these two namespaces have?
template <typename T>
struct IsSpecialized <T, typename enable_if<is_integral<T> >::type> {
static const bool specialized = true;
};

Shouldn't it be:

template <typename T> struct IsSpecialized
<T, typename std::enable_if<boost::is_integral<T>::value>::type>
{ static const bool specialized = true; };

Note 'boost::is_integral<T>::value' may be 'true' that 'std::enable_if'
expects. 'boost::is_integral<T>' is just a type. I don't know how it
works with 'void' but I suspect that "I am using all namespaces
I have heard of" idiom achieved it somehow.
 
S

SG

I've come across a bizarre case where the member type of enable_if
seems to control whether a partial specialization is found. If I
pass any type other than the default of `void`, the specialization
is hidden.

It is not hidden. It's just a specialization for a different case you
don't make use of in main. See below.

Let me cut down your example code a bit ...
template <typename T, typename Enable=void>
struct IsSpecializedInt
{
    static const bool specialized = false;
};

// note the `int` below:
template <typename T>
struct IsSpecializedInt <T,
typename boost::enable_if<boost::is_integral<T>, int>::type>
{ // ^^^
    static const bool specialized = true; // so, this is a specialization for
}; // isSpecializedInt<T,int>
// ^^^
int main(int argc, char **argv)
{
    cout << IsSpecializedInt<int>::specialized << endl;
// ^
// Since you're not specifying the second parameter,
// void will be used because this is the default.
}

You're expecting this to print true but it will print false. Here's
why: Your specialization is valid (it will not be SFINAEed out). But
it is a specialization for IsSpecialized<T,int> where T is an integral
and not IsSpecialized<T,void>. However, In your main program you make
use of IsSpecialized<int,void> because void is the default for the 2nd
parameter which is not specialized because the only specialization you
have is with int as second parameter.
 
T

trbabb

it is a specialization for IsSpecialized<T,int> where T is an integral
and not IsSpecialized<T,void>. However, In your main program you make
use of IsSpecialized<int,void> because void is the default for the 2nd
parameter which is not specialized because the only specialization you
have is with int as second parameter.

This makes perfect sense. Now that you've pointed it out, I don't quite know why this wasn't obvious to me-- I think the second argument to the specialization template was taking the place of the default template argument in my mind.

Thanks!
-tb
 
T

trbabb

Writing that may get your contract terminated immediately.

Yes, but this is only a toy example, and it makes for a terser read.
Shouldn't it be:



template <typename T> struct IsSpecialized

<T, typename std::enable_if<boost::is_integral<T>::value>::type>

{ static const bool specialized = true; };



Note 'boost::is_integral<T>::value' may be 'true' that 'std::enable_if'
expects. 'boost::is_integral<T>' is just a type. I don't know how it
works with 'void' but I suspect that "I am using all namespaces
I have heard of" idiom achieved it somehow.

In a word, No. boost::enable_if<> expects a type, namely something that inherits from boost::true_type. It is boost::enable_if_c<> which expects something of type bool.
 
Ö

Öö Tiib

Yes, but this is only a toy example, and it makes for a terser read.

Maybe for a more indolent write but certainly for a crappier read. Like
you saw further I got confused if you used std::enable_if or
boost::enable_if.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,187
Members
46,730
Latest member
AudryNolan

Latest Threads

Top