вопрос по SFINAE и выбору функции
От: sergii.p  
Дата: 21.02.19 09:04
Оценка:
всем привет.
Попытался сделать простое разделение функций для enum и остальных типов и натолкнулся на не совсем понятные ошибки.
Для начала объявляю что-то типа концепции

template<typename T>
using IsEnumConcept = std::enable_if_t<std::is_enum<T>::value>;
template<typename T>
using IsNotEnumConcept = std::enable_if_t<!std::is_enum<T>::value>;


IsEnumConcept — для enum, IsNotEnumConcept — для всех остальных
Теперь пытаюсь сделать две функции, которые ничего не принимают (в реальности они принимают tuple, но для примера это не важно) и ничего не возвращают, но для enum они должны делать что-то отличное.

template<typename T, typename = IsEnumConcept<T>>
void out()
{
    cout<<"for enum\n";
}

template<typename T, typename = IsNotEnumConcept<T>>
void out()
{
    cout<<"for other\n";
}


в данном случае компилятор выдаёт ошибку redefinition of ‘template<class T, class> void out() Вроде ошибка вполне логичная и можно успокоиться, но немного переписываем пример

template<typename T>
IsEnumConcept<T> out()
{
    cout<<"for enum\n";
}

template<typename T>
IsNotEnumConcept<T> out()
{
    cout<<"for other\n";
}


и тут компилятор говорит, что всё ок, так дальше и пиши.
Во-первых, не понятно почему компилируется второй пример. Обе функции разворачиваются в void out() На лицо явное redefinition.
Во-вторых, не ясно, чем отличается первый пример от второго с точки зрения компилятора.

Первый вариант более читабелен, так что в приоритете. Хотелось бы его довести до рабочего состояния. На обоих компиляторах (студийный и gcc) поведение одинаковое, так что на них грешу в последнюю очередь.
Re: вопрос по SFINAE и выбору функции
От: sergii.p  
Дата: 21.02.19 09:22
Оценка:
тут полный пример https://ideone.com/DG69GW
Re: вопрос по SFINAE и выбору функции
От: niXman Ниоткуда https://github.com/niXman
Дата: 21.02.19 09:43
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Во-первых, не понятно почему компилируется второй пример. Обе функции разворачиваются в void out() На лицо явное redefinition.


нет. посмотрите реализацию enable_if

хотя, возможно, я неправильно понял вопрос...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 21.02.2019 9:46 niXman . Предыдущая версия .
Re: вопрос по SFINAE и выбору функции
От: _NN_ www.nemerleweb.com
Дата: 21.02.19 10:28
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Во-первых, не понятно почему компилируется второй пример. Обе функции разворачиваются в void out() На лицо явное redefinition.

SP>Во-вторых, не ясно, чем отличается первый пример от второго с точки зрения компилятора.
Разворачиваются то да, но есть нюанс.
В случае когда выражение получается ложным как is_enum для int, другая функция out просто выбрасывается из списка подходящих функций и у компилятора остаётся только один кандидат.
Поэтому и нет неоднозначностей.

Варианты решения:
1. Использовать параметр по умолчанию:
template<typename T>
void out(std::enable_if_t<std::is_enum<T>::value, T>* = nullptr)
{
    std::cout << "enum\n";
}

template<typename T>
void out(std::enable_if_t<!std::is_enum<T>::value, T>* = nullptr)
{
    std::cout << "other\n";
}


2. Обернуть вызов в класс и частично специализировать его:
template<typename T> class Out;

template<typename T> class Out<enable_if_t<is_enum<T>, T>> {public: static void out() { .. } }
template<typename T> class Out<enable_if_t<!is_enum<T>, T>> {public: static void out() { .. } }

Out<int>::out();
Out<int>::out();


SP>Первый вариант более читабелен, так что в приоритете. Хотелось бы его довести до рабочего состояния. На обоих компиляторах (студийный и gcc) поведение одинаковое, так что на них грешу в последнюю очередь.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: вопрос по SFINAE и выбору функции
От: Jack128  
Дата: 21.02.19 11:03
Оценка: 6 (2)
Здравствуйте, sergii.p, Вы писали:

Вот буквальная первая же ссылка в гугде по enable_if выдает:

A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

Re[2]: вопрос по SFINAE и выбору функции
От: sergii.p  
Дата: 21.02.19 11:25
Оценка:
Здравствуйте, Jack128, Вы писали:

да, это то, что нужно. Учитывая тривиальную реализацию enable_if в документацию даже не думал смотреть.
Re: вопрос по SFINAE и выбору функции
От: rg45 СССР  
Дата: 21.02.19 17:25
Оценка: 3 (1)
Здравствуйте, sergii.p, Вы писали:

SP>Первый вариант более читабелен, так что в приоритете. Хотелось бы его довести до рабочего состояния. На обоих компиляторах (студийный и gcc) поведение одинаковое, так что на них грешу в последнюю очередь.


Я тебе предлагаю рассмотреть небольшой рефакторинг, а именно, реализовать шаблонную функцию через вспомогательный шаблонный класс. Это достаточно распространенный подход, позволяющий обходить самые разные проблемы, связанные с тем, что шаблоны функций не допускают частичной специализации, а шаблоны классов допускают:

https://ideone.com/NOE8SR

#include <iostream>

template<typename T>
using IsEnumConcept = std::enable_if_t<std::is_enum<T>::value>;

template<typename T, typename = void>
struct OutImpl
{
    void operator()() const { std::cout<<"for not enum\n"; }    
};

template <typename T>
struct OutImpl<T, IsEnumConcept<T>>
{
    void operator()() const { std::cout<<"for enum\n"; }    
};

template<typename T>
void out()
{
    OutImpl<T>()();
}

enum class MyEnum{};

int main() {
    out<MyEnum>();
    out<int>();
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 21.02.2019 18:25 rg45 . Предыдущая версия . Еще …
Отредактировано 21.02.2019 17:25 rg45 . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.