boost :: enable_if отсутствует в сигнатуре функции - PullRequest
7 голосов
/ 05 января 2012

Это просто вопрос стиля: мне не нравится способ C ++ для шаблонного метапрограммирования, который требует, чтобы вы использовали возвращаемый тип или добавили дополнительный фиктивный аргумент для трюков с SFINAE.Итак, идея, которую я придумал, заключается в том, чтобы поместить элемент SFINAE в само определение аргументов шаблона, например:

#include <iostream>
#include <boost/type_traits/is_array.hpp>
#include <boost/utility/enable_if.hpp>
using namespace std;

template <typename T, typename B=typename boost::enable_if< boost::is_array<T> >::type > void asd(){
    cout<<"This is for arrays"<<endl;
}

template <typename T, typename B=typename boost::disable_if< boost::is_array<T> >::type > void asd(){
    cout<<"This is for NON arrays"<<endl;
}

int main() {
    asd<int>();
    asd<int[]>();
}

В этом примере g ++ жалуется:

../src/afg.cpp:10:97: ошибка: переопределение 'template void asd ()'

SFINAE там само по себе работает, потому что, если я удалю, например, тот, с disable_if,ошибка компилятора:

.. / src / afg.cpp: 15: 12: ошибка: нет соответствующей функции для вызова 'asd ()'

то, что я хочу.

Итак, есть ли способ выполнить SFINAE не в «нормальной» сигнатуре функции, то есть тип возвращаемого значения + список аргументов?

РЕДАКТИРОВАТЬ: Это взакончите то, что я собираюсь попробовать в реальном коде:

#include <iostream>
#include <type_traits>
using namespace std;

template <typename T, typename enable_if< is_array<T>::value, int >::type =0 > void asd(){
    cout<<"This is for arrays"<<endl;
}

template <typename T, typename enable_if< !is_array<T>::value, int >::type =0 > void asd(){
    cout<<"This is for NON arrays"<<endl;
}

int main() {
    asd<int[]>();
    asd<int>();
}

Я использую материал c ++ 0x вместо boost, потому что, пока мне нужен c ++ 0x для использования значений по умолчанию аргументов шаблона, яне вижу причин использовать boost, который является его предшественником.

Ответы [ 5 ]

9 голосов
/ 10 февраля 2012

Ну, я обычно использую эти макросы, чтобы сделать конструкции enable_if намного чище (они даже работают в большинстве компиляторов C ++ 03):

#define ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE(...) __VA_ARGS__>::type
#define FUNCTION_REQUIRES(...) typename boost::enable_if<boost::mpl::and_<__VA_ARGS__, boost::mpl::bool_<true> >, ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE
#define EXCLUDE(...) typename boost::mpl::not_<typename boost::mpl::or_<__VA_ARGS__, boost::mpl::bool_<false> >::type >::type

Тогда вы бы определили свою функцию следующим образом:

template <typename T >
FUNCTION_REQUIRES(is_array<T>)
(void) asd(){
    cout<<"This is for arrays"<<endl;
}

template <typename T >
FUNCTION_REQUIRES(EXCLUDE(is_array<T>))
(void) asd(){
    cout<<"This is for NON arrays"<<endl;
}

Единственное, вам нужно поставить круглые скобки вокруг возвращаемого типа.Если вы забудете их, компилятор скажет что-то вроде 'ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE' не определено.

8 голосов
/ 05 января 2012

Поскольку C ++ 11 сделал это возможным, я всегда использую enable_if (или наоборот disable_if) внутри аргументов шаблона, как вы делаете. Если / когда есть несколько перегрузок, я использую фиктивные аргументы шаблона по умолчанию, что делает списки параметров шаблона разными по арности. Итак, для повторного использования вашего примера это будет:

template<
    typename T
    , typename B = typename boost::enable_if<
        boost::is_array<T>
    >::type
>
void asd() {
    cout << "This is for arrays" << endl;
}

template<
    typename T
    , typename B = typename boost::disable_if<
        boost::is_array<T>
    >::type
    , typename = void
>
void asd() {
    cout << "This is for arrays" << endl;
}

Еще одна альтернатива, чтобы не испортить возвращаемый тип (который в некоторых случаях недоступен, например, операторы преобразования), существовавший с C ++ 03, - использовать аргументы по умолчанию:

template<typename T>
void
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr);

Я не использую эту форму, так как мне не нравится «возиться» с типами аргументов так же, как с типом возвращаемого значения, и по соображениям согласованности (поскольку это не выполнимо во всех случаях).

6 голосов
/ 06 января 2012

Аргументы шаблона по умолчанию не являются частью сигнатуры шаблонов функций.Но тип параметров шаблона есть.Таким образом, вы можете сделать следующее и иметь возможность его перегрузить

template <
  typename T,
  typename boost::enable_if< 
    boost::is_array<T>, int 
  >::type = 0
> 
void asd() {
    cout<<"This is for arrays"<<endl;
}

template <
  typename T, 
  typename boost::disable_if< 
    boost::is_array<T>, int 
  >::type = 0 
>
void asd() {
    cout<<"This is for arrays"<<endl;
}
2 голосов
/ 05 января 2012

Итак, есть ли способ выполнить SFINAE не в «нормальной» сигнатуре функции, то есть тип возвращаемого значения + список аргументов?

Что ж, есть способ получитьтот же результат без использования SFINAE - перегрузка:

#include <iostream>
#include <type_traits>

void asd_impl(std::true_type&&)
{
    std::cout << "This is for arrays\n";
}

void asd_impl(std::false_type&&)
{
    std::cout << "This is not for arrays\n";
}

template<typename T>
void asd()
{
    asd_impl(std::is_array<T>());
}

int main()
{
    asd<int>();
    asd<int[]>();
}

Этот стиль гораздо более читабелен для IMO и широко используется в таких библиотеках, как Boost . Spirit , потому что он быстрее компилируется и работает лучше с компиляторами, имеющими поддержку менее звездных шаблонов / SFINAE (например, VC ++ и Sun Studio).

Онлайн-демонстрация.

2 голосов
/ 05 января 2012

Это может быть не совсем то, что вы просите, но как насчет старой доброй специализации шаблонов?

template<typename T>
struct asd
{
    static void fgh()
    {
        std::cout << "not an array\n";
    }
};

template<typename T>
struct asd<T[]>
{
    static void fgh()
    {
        std::cout << "an array of unknown size\n";
    }
};

template<typename T, size_t N>
struct asd<T[N]>
{
    static void fgh()
    {
        std::cout << "an array of known size\n";
    }
};

int main()
{
    asd<int>::fgh();
    asd<int[]>::fgh();
    asd<int[42]>::fgh();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...