Вопрос по c++, c++11 – Как использовать enable_if для взаимоисключающих шаблонов функций, не являющихся членами?

6

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

#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>() )
{ /*...*/ }

который я прочитал (здесь на S.O.), чтобы решить подобные проблемы для шаблонов функций-членов. (Иногда респонденты меняли функцию-член на шаблон функции-члена, чтобы включить это.) Но ошибки для меня не меняются. Должен ли я перейти к положениюenable_if в тип возврата?

О, выражение возвращаемого типа должно исключать этот оператор, когда два типа элементов нельзя сравнивать. Будет ли это на самом деле работать? Это совместимо с положениемenable_if там тоже?

Что именно является ошибкой неоднозначности? Kerrek SB

Ваш Ответ

1   ответ
11

Интересно, чтоопределенный парень здесь на SO недавно написал пост в блоге, демонстрируя приятную технику SFINAE в стиле C ++ 11, которая позволяет легко перегружать функции. Техника и объяснение предоставляютсяВот.

Короче говоря, ваш код дает сбой, потому что оба шаблона, когда анализируются первый раз и когда разрешаются независимые объявления, являютсяexactly то же самое, по типу. Как и в случае с аргументами функции по умолчанию, аргументы шаблона по умолчанию заменяются только при фактическом вызове функции. Вот как оба шаблона выглядят для компилятора в точке объявления:

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? В этом случае ни одна перегрузка не является жизнеспособной.

Вот Это Да! Мой разум был взорван. Я думал о мультисостоянии (достаточно легко с variadic), но ... объединяя так много техники и умудряясь попасть в такое синтаксическое место. Удивительно. Спасибо, что поделились статьей.
Версияoperator == в первом блоке описан случай, когда вторые параметры шаблона класса равны. Две версии, о которых я спрашиваю, являются дополнением к первой версии и не заменяют ее. CTMacUser
@CTMacUser Да, нет больших проблем с использованиемint или жеstd::nullptr_t, Мне просто нравится быть очень осторожным :)
Этоvery интересная статья. Для моего кода я использовалtypename std::enable_if<(Lt < Lu)>::type... в качестве окончательного параметра шаблона. Мой компилятор, GCC-4.7 (32-битный PowerPC, от MacPorts), принял этот параметр, хотя он техническиvoid... (неполный тип), и в статье говорится, чтоvoid не может быть использован! Я планировал использоватьstd::nullptr_t или жеint так как я не хотел добавлять одноразовый тип в библиотечный код (но не берите в голову). CTMacUser

Похожие вопросы