Search code examples
c++templatesboosttemplate-specializationenable-if

Conditional template specialization on method of a non template class


I am trying to use boost::enable_if to conditional specialize a method of a non-templated class, but failing at it.

//OSSpecific.h
...
//If some OS
typedef unsigned int UINT;
//else
typedef unsigned long long UINT;
...

//Foo.h
...
#include <OSSpecific.h>
...
class Foo
{
   public:
          ...
          template <typename T>
          returnThis<T>* bar();
}
/******************************************************************/
//Foo.cpp
...
template<>
returnThis<float>* bar()
{
}

//Use this if some condition is true
template<>
returnThis<int>* Foo::bar<boost::disable_if_c<boost::is_same<int, UINT>::value >::type>()
{
    //Do something
}

    //Use this if some condition is true
template<>
returnThis<long long>* Foo::bar<boost::disable_if_c<boost::is_same<long long, UINT>::value >::type>()
{
    //Do something
}

I get the following error:

Foo.cpp : error C2785: 'returnType<T> *Foo::bar(void)' and 'returnType<T> *Foo::bar(void)' have different return types         
            with                                                                                                                                                                                                                                          
            [                                                                                                                                                                                                                                             
                T=int                                                                                                                                                                                                                                
            ]                                                                                                                                                                                                                                             
            Foo.h : see declaration of 'Foo::bar'                                                
            Foo.cpp : see declaration of 'Foo::bar'                                                                                                                     
    Foo.cpp : error C2910: 'Foo::bar' : cannot be explicitly specialized  

Any pointers where I am going wrong?

EDIT: I tried to simplify my question too much. Adding more relevant detail.


Solution

    1. You must follow ODR - one definition rule. So you have to have declarations of your functions also in header file.

    2. To use SFINAE your functions need to be template functions - not fully specialized template functions - but couple of different template functions.

    So - see header file. Note that you have 3 different functions here - they are not specializations of each others. Thanks to SFINAE first is only active if T==float. 2nd and 3rd if T==UINT - and the distinction between them is this condition: UINT==unsigned int.

    class Foo
    {
    public:
    
        template <typename T>
        typename boost::enable_if<boost::is_same<T,float>,returnThis<float>*>::type 
        bar();
    
        template <typename T>
        typename boost::enable_if_c<boost::is_same<T,UINT>::value and boost::is_same<UINT,unsigned int>::value,
        returnThis<UINT>*>::type 
        bar();
    
        template <typename T>
        typename boost::enable_if_c<boost::is_same<T,UINT>::value and not boost::is_same<UINT,unsigned int>::value,
                 returnThis<UINT>*>::type 
        bar();
    };
    

    Then possible usage file:

    int main() {
        Foo f;
        f.bar<float>();
        f.bar<UINT>();
        return 0;
    }
    

    If UINT==unsigned int this code will call 1st and 2nd functions. If UINT!=usinged int the 1st and 3rd functions will be called.

    Then your source file (Foo.cpp):

    template <typename T>
    typename boost::enable_if<boost::is_same<T,float>,returnThis<float>*>::type 
    Foo::bar()
    {
        cout << "bar<T==float>()\n";
        return 0;
    }
    
    template <typename T>
    typename boost::enable_if_c<boost::is_same<T,UINT>::value and boost::is_same<UINT,unsigned int>::value,
    returnThis<UINT>*>::type 
    Foo::bar()
    {
        cout << "bar<T==UINT and UINT==usigned int>()\n";
        return 0;
    }
    
    template <typename T>
    typename boost::enable_if_c<boost::is_same<T,UINT>::value and not boost::is_same<UINT,unsigned int>::value,
    returnThis<UINT>*>::type 
    Foo::bar()
    {
        cout << "bar<T==UINT and UINT!=usigned int>()\n";
        return 0;
    }
    

    Since these functions do not actually depends on T - you can require from compiler to have generated code in your dedicated cpp file by template explicit instantiation instructions:

    template returnThis<float>* Foo::bar<float>();
    template returnThis<UINT>* Foo::bar<UINT>();
    

    See IDEONE working example