Ограничение доступа к функции

У меня есть общий класс с функцией, которую я хочу ограничить только экземплярами типов с плавающей запятой во время компиляции. Как показано в примере ниже:

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

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

Как заставить компилятор отказаться от использования some_method для ClassName типов, отличных от плавающей запятой?

Я смотрел на SFINAE, но я просто не могу заставить его работать, поэтому после нескольких часов неудач я прошу вашей помощи.

Спасибо :)


person Jesper    schedule 07.08.2013    source источник
comment
Что вы пробовали? Вероятно, вы могли бы скомбинировать что-то вместе с std::is_floating_point и std::enable_if.   -  person juanchopanza    schedule 07.08.2013
comment
В настоящее время я оборачиваю функцию в другую функцию, например так: void some_other_method() { some_method(std::is_floating_point<T>()); } void some_method() { } Это работает, но мне нужно решение, в котором мне не нужно оборачивать функцию.   -  person Jesper    schedule 07.08.2013
comment
@juanchopanza Вы можете заставить свой пример работать, если сделаете some_method шаблон. Я ищу предыдущий ответ, который объясняет механизмы, о которых здесь идет речь.   -  person R. Martinho Fernandes    schedule 07.08.2013
comment
Специализация шаблонов также является действенным методом для достижения этой цели.   -  person cdhowie    schedule 07.08.2013
comment
@R.MartinhoFernandes Я исправил свой пример, но тем временем кажется, что другие пришли к тому же решению.   -  person juanchopanza    schedule 07.08.2013


Ответы (6)


Вы можете использовать комбинацию std::is_floating_point и std::enable_if, чтобы включить функцию только для типов с плавающей запятой:

#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
}
person juanchopanza    schedule 07.08.2013
comment
@PetrBudnik был первым, но этот ответ более сложный, поэтому он принят. Ваше здоровье! - person Jesper; 07.08.2013

Используйте static_assert, если ваш компилятор поддерживает С++ 11

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

Тогда будет ошибка компилятора, если вы попытаетесь вызвать этот метод для параметра без плавающей запятой.

И для не с плавающей запятой:

static_assert( !std::is_floating_point<T>::value, "and only for non-floating point" );
person user1837009    schedule 07.08.2013
comment
Вы должны отрицать условие. - person jrok; 07.08.2013
comment
@jrok: Спасибо, я редактирую свой ответ. - person user1837009; 08.08.2013

Что-то вроде этого:

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

РЕДАКТИРОВАТЬ, чтобы уточнить. Это приведет к следующему. Компилятор сгенерирует some_method() только для ClassName с параметром шаблона с плавающей запятой. Он не будет создан для неплавающих типов и приведет к ошибке времени компиляции.

#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 );
}
person lapk    schedule 07.08.2013
comment
Будет ли T в этом шаблоне == T в шаблоне класса? - person Jesper; 07.08.2013
comment
@Jesper Просто сделайте его типом по умолчанию. Я дал общее представление. См. Редактировать. - person lapk; 07.08.2013

void some_method(){

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

Я тоже пробовал с boost::is_floating_point :-

#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;
*/
}
person P0W    schedule 07.08.2013
comment
Не совсем то же самое. Примечание. Как заставить компилятор отклонить использование some_method для ClassName типов, отличных от плавающей запятой?. - person R. Martinho Fernandes; 07.08.2013
comment
@Mgetz да, есть решение (см. другие ответы). Вы можете получить те же результаты, которых может достичь предложенный static if, не имея его. Вы можете процитировать меня по этому поводу. - person R. Martinho Fernandes; 07.08.2013
comment
@Mgetz нет, это не так. Определения функций не создаются, если они не используются. См. coliru.stacked-crooked.com/, coliru.stacked-crooked.com/, coliru.stacked-crooked.com/ и coliru.stacked-crooked.com/. - person R. Martinho Fernandes; 07.08.2013

Как подробно описано в этом ответе, вам нужно, чтобы функция-член была шаблоном для работы SFINAE (Живой пример в 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
    }
};
person Casey    schedule 07.08.2013

Обновление согласно комментарию Р. Мартиньо Фернандеша.

#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;
}
person Mike Kinghan    schedule 07.08.2013
comment
Для чего нужен SFINAE, если вы собираетесь утверждать противоположное разрешающему условию? - person R. Martinho Fernandes; 07.08.2013
comment
Когда SFINAE выбирает вариант не-с плавающей запятой-T, который должен терпеть неудачу, тогда я утверждаю, что R(=T) является с плавающей запятой. Так что блюет. - person Mike Kinghan; 07.08.2013
comment
Конечно, но это также будет блевать, если вы просто поместите утверждение в функцию без каких-либо SFINAE. - person R. Martinho Fernandes; 07.08.2013