3

I have a generic class with a function that I want to restrict to instances of floating point types only, at compile time. As shown in the example below:

template <typename T>
class ClassName
{
    // instance variables, etc..

    void some_method()
    {
        // do stuff, but only for floating point types
    }
}

How do I make the compiler reject the usage of some_method for ClassName of non-floating point types?

I have been looking at SFINAE but I simply can't get it to work, so after several hours of failing I'm asking for your help.

Thanks :)

Mgetz
  • 5,108
  • 2
  • 33
  • 51
Jesper
  • 91
  • 3
  • 1
    What have you tried? You could probably put something together with [`std::is_floating_point`](http://en.cppreference.com/w/cpp/types/is_floating_point) and [`std::enable_if`](http://en.cppreference.com/w/cpp/types/enable_if). – juanchopanza Aug 07 '13 at 16:24
  • Currently I'm wrapping the function in another function like so: `void some_other_method() { some_method(std::is_floating_point()); }` `void some_method() { }` This works, but I would like a solution where I don't have to wrap the function. – Jesper Aug 07 '13 at 16:35
  • @juanchopanza You can make your example work if you make `some_method` a template, though. I'm looking for a previous answer that explains the mechanisms at stake here. – R. Martinho Fernandes Aug 07 '13 at 16:42
  • 1
    Template specialization is a valid technique to achieve this too. – cdhowie Aug 07 '13 at 16:43
  • @R.MartinhoFernandes I fixed my example, but in the meantime it seems others have arrived at the same solution. – juanchopanza Aug 07 '13 at 16:52

6 Answers6

6

Use static_assert, if your compiler support c++11

void some_method()
{
    static_assert( std::is_floating_point<T>::value, "Only for floating points" );
    // do stuff, but only for floating point types
}

Then there will be compiler error if you try call this method for non floating points parameter.

And for not-floating point:

static_assert( !std::is_floating_point<T>::value, "and only for non-floating point" );
user1837009
  • 485
  • 4
  • 11
6

You can use a combination of std::is_floating_point and std::enable_if to only enable the function for floating point types:

#include <type_traits>

template <typename T>
class ClassName
{
    // instance variables, etc..
 public:
  template<typename T2 = T,
           typename = typename std::enable_if< std::is_floating_point<T2>::value >::type>
  void some_method()
  { 
    // do stuff, but only for floating point types
  } 
};

int main()
{
  ClassName<double> d; // OK
  d.some_method();     // OK
  ClassName<int> i;    // OK
  i.some_method();     // ERROR
}
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
3

Something like this:

template< typename Tdummy = T, typename = typename std::enable_if< std::is_floating_point< Tdummy >::value >::type >
 void some_method()
{
}

EDIT to elaborate. This will result in the following. Compiler will generate some_method() only for ClassName with floating-point template parameter. It will not be generated for non-floating types and will result in a compile-time error.

#include <type_traits>

template <typename T>
class ClassName
{
    // instance variables, etc..
    template< typename Tdummy = T, typename = typename std::enable_if< std::is_floating_point< Tdummy >::value >::type >
    void some_method()
    {
        // do stuff, but only for floating point types
    }

 void some_general_method
  {
   // general stuff for all types
  }
};

int main()
{
 ClassName< float > bar;
 ClassName< int > foo;

 bar.some_general_method(); // OK
 foo.some_general_method(); // OK

 bar.some_method(); // OK
 foo.some_method(); // Compile-time ERROR 

 return( 0 );
}
lapk
  • 3,838
  • 1
  • 23
  • 28
2
void some_method(){

    if (std::is_floating_point<T>::value)
    {
        // do stuff, but only for floating point types
    }
    else
    {
        return;
    }
}

I tried with boost::is_floating_point too :-

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_floating_point.hpp>

template <typename T>
class ClassName {
typename boost::enable_if<boost::is_floating_point<T> >::type
some_method(const T & t)
{

}
};

int main()
{
ClassName<float> p; //Compiles

/* Following throws error, 
error: no type named 'type' in 
    'struct boost::enable_if<boost::is_floating_point<int>, void>'
ClassName<int> q;
*/
}
P0W
  • 46,614
  • 9
  • 72
  • 119
  • Not quite the same. Note "How do I make *the compiler reject* the usage of some_method for ClassName of non-floating point types?". – R. Martinho Fernandes Aug 07 '13 at 16:31
  • @Mgetz yes there is a solution (see other answers). You can get the same results that the proposed `static if` can achieve without having it. You can quote me on that. – R. Martinho Fernandes Aug 07 '13 at 16:37
  • @Mgetz no, it does not. Functions definitions are not instantiated unless used. See http://coliru.stacked-crooked.com/view?id=46f36b442ad191ec5bb171388773d46d-6bd941bd569898d6f94f2545382ce976, http://coliru.stacked-crooked.com/view?id=bb47a5533c53dc8a49441d51bcfaef8c-6bd941bd569898d6f94f2545382ce976, http://coliru.stacked-crooked.com/view?id=a520465ed8ca7d7c84237dd3450542ec-6bd941bd569898d6f94f2545382ce976, and http://coliru.stacked-crooked.com/view?id=8482aeca87df479ac97193bf36b7dbd4-6bd941bd569898d6f94f2545382ce976 as examples. – R. Martinho Fernandes Aug 07 '13 at 16:43
1

As detailed in this answer, you need the member function to be a template for SFINAE to work (Live example at Coliru):

template <typename T>
class ClassName
{
    // instance variables, etc..

public:
    template <typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
    void some_method()
    {
        // do stuff, but only for floating point types
    }
};
Community
  • 1
  • 1
Casey
  • 41,449
  • 7
  • 95
  • 125
0

Update per R. Martinho Fernandes comment

#include <type_traits>

template <typename T>
struct ClassName
{
    // instance variables, etc..

    template<typename R = T>
    void some_method()
    {
        static_assert(std::is_floating_point<R>::value,
            "ClassName<T>::some_method is implemented only for floating "
                "point T");
        // do stuff, but only for floating point types
    }
};

int main()
{   
    ClassName<float> f;
    f.some_method();
    ClassName<int> i;
    i.some_method(); // <-- static_asserts here
    return 0;
}
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182