Я пытаюсь получить простой пример для работы, чтобы понять, как использовать std::enable_if
. Прочитав этот ответ , я подумал, что не должно быть слишком сложно привести простой пример. Я хочу использовать std::enable_if
для выбора между двумя функциями-членами и разрешить использовать только одну из них.
К сожалению, следующее не компилируется с gcc 4.7 и после нескольких часов попыток я спрашиваю вас, ребята, в чем моя ошибка.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
gcc сообщает о следующих проблемах:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Почему g ++ не удаляет неправильные экземпляры для второй функции-члена? Согласно стандарту, std::enable_if< bool, T = void >::type
существует только тогда, когда логический параметр шаблона имеет значение true. Но почему g ++ не считает это SFINAE? Я думаю, что сообщение об ошибке перегрузки происходит из-за того, что g ++ не удаляет вторую функцию-член и считает, что это должно быть перегрузкой.
std::is_same< T, int >::value
и! std::is_same< T, int >::value
это дает тот же результат.Ответы:
SFINAE работает только в том случае, если подстановка при выводе аргумента шаблона аргумента делает конструкцию некорректной. Там нет такой замены.
Это потому, что когда создается экземпляр шаблона класса (что происходит, когда вы создаете объект типа
Y<int>
среди других случаев), он создает все объявления своих членов (не обязательно их определения / тела!). Среди них также его шаблоны участников. Обратите внимание, чтоT
известно тогда, и!std::is_same< T, int >::value
дает ложь. Таким образом, он создаст класс,Y<int>
который содержитstd::enable_if<false>::type
Обращается к не-существующему типу, так что декларация плохо сформирована. И поэтому ваша программа недействительна.Вам необходимо сделать так, чтобы шаблоны элементов
enable_if
зависели от параметра самого шаблона элемента. Тогда объявления являются действительными, потому что весь тип все еще зависит. Когда вы пытаетесь вызвать один из них, вывод аргументов для их аргументов шаблона происходит, а SFINAE происходит, как ожидалось. Смотрите этот вопрос и соответствующий ответ о том, как это сделать.источник
Y
класса шаблона, компилятор фактически не скомпилирует функции-члены шаблона; тем не менее, компилятор выполнит подстановкуT
DECLARATIONS в шаблоне элемента, чтобы эти шаблоны-члены могли быть созданы позже. Эта точка отказа не является SFINAE, потому что SFINAE применяется только при определении набора возможных функций для разрешения перегрузки , а создание экземпляра класса не является случаем определения набора функций для разрешения перегрузки. (Или так я думаю!)Я сделал этот короткий пример, который также работает.
Прокомментируйте, если вы хотите, чтобы я уточнил. Я думаю, что код более или менее говорит само за себя, но опять же я сделал это, чтобы я мог ошибаться :)
Вы можете увидеть это в действии здесь .
источник
error C4519: default template arguments are only allowed on a class template
,Q
, даже если он равенT
?test
функцию-член. Оба не могут существовать одновременно.Q
просто пересылает тип шаблона классаT
. Вы можете удалить шаблон классаT
следующим образом: cpp.sh/4nxw, но это своего рода побеждает цель.Для тех опоздавших, которые ищут решение, которое «просто работает»:
Компилировать с:
Бег дает:
источник
std::enable_if_t
вresolvedType
.Из этого поста:
Но можно сделать что-то вроде этого:
источник
Одним из способов решения этой проблемы, специализацией функций-членов, является помещение специализации в другой класс, а затем наследование от этого класса. Возможно, вам придется изменить порядок наследования, чтобы получить доступ ко всем другим базовым данным, но этот метод работает.
Недостаток этого метода заключается в том, что если вам нужно протестировать множество разных вещей для разных функций-членов, вам придется создать класс для каждой и связать его в дереве наследования. Это верно для доступа к общим членам данных.
Пример:
источник
Логическое значение должно зависеть от выводимого параметра шаблона. Таким образом, простой способ исправить это использовать логический параметр по умолчанию:
Однако это не сработает, если вы хотите перегрузить функцию-член. Вместо этого лучше всего использовать
TICK_MEMBER_REQUIRES
из библиотеки Tick :Вы также можете реализовать свой собственный член требует макрос, как это (на тот случай, если вы не хотите использовать другую библиотеку):
источник
Вот мой минималистский пример использования макроса. Используйте двойные скобки
enable_if((...))
при использовании более сложных выражений.источник