Как использовать enable_if для взаимоисключающих шаблонов функций, не являющихся членами?
Я пытаюсь написать шаблоны функций, не являющихся членами оператора, такие как:
#include <utility>
template < typename T, unsigned L >
class MyType;
template < typename T, typename U, unsigned L >
auto operator ==( MyType<T,L> const &l, MyType<U,L> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
Но когда я пытаюсь справиться, когда l
а также r
имеют разные длины:
template < typename T, unsigned Lt, typename U, unsigned Lu, class Enable = typename std::enable_if<(Lt < Lu)>::type >
auto operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
template < typename T, unsigned Lt, typename U, unsigned Lu, class Enable = typename std::enable_if<(Lt > Lu)>::type >
auto operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
Я получаю ошибки неоднозначности. Я попробовал что-то вроде:
template < typename T, unsigned Lt, typename U, unsigned Lu, bool B = (Lt < Lu), class Enable = typename std::enable_if<B>::type >
auto operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
template < typename T, unsigned Lt, typename U, unsigned Lu, bool B = (Lt > Lu), class Enable = typename std::enable_if<B>::type >
auto operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
который я прочитал (здесь, на SO), чтобы решить подобные проблемы для шаблонов функций-членов. (Иногда респонденты меняли функцию-член на шаблон функции-члена, чтобы включить это.) Но ошибки для меня не меняются. Должен ли я перейти к положению enable_if
в тип возврата?
О, выражение возвращаемого типа должно исключать этот оператор, когда нельзя сравнивать два типа элементов. Будет ли это на самом деле работать? Совместимо ли это с положением enable_if
там тоже?
1 ответ
Интересно, что R. Martinho Fernandes, недавно написал пост в блоге, демонстрируя приятную технику SFINAE в стиле C++ 11, которая позволяет легко перегружать функции. Техника и объяснение представлены здесь.
Короче говоря, ваш код дает сбой, потому что оба шаблона, когда анализируются первый раз и когда разрешаются независимые объявления, абсолютно одинаковы по типу. Как и в случае с аргументами функции по умолчанию, аргументы шаблона по умолчанию заменяются только при фактическом вызове функции. Вот как оба шаблона выглядят для компилятора в точке объявления:
template<class T, unsigned Lt, class U, unsigned Lu, class Enable>
auto operator==(MyType<T,Lt> const& l, MyType<U,Lu> const& r);
Следующий код должен достичь того, что вы хотите:
namespace detail{
enum class enabler{};
}
template<bool B, class T = detail::enabler>
using EnableIf = typename std::enable_if<B, T>::type;
template < typename T, unsigned Lt, typename U, unsigned Lu, EnableIf<(Lt < Lu)>...>
auto operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
template < typename T, unsigned Lt, typename U, unsigned Lu, EnableIf<(Lt > Lu)>...>
auto operator ==( MyType<T,Lt> const &l, MyType<U,Lu> const &r )
-> decltype( std::declval<T>() == std::declval<U>() )
{ /*...*/ }
Однако остается один вопрос... что должно произойти, если Lt == Lu
? В этом случае ни одна перегрузка не является жизнеспособной.