Не могу ограничить перегрузку шаблонной функции `operator +` для моей иерархии классов, используя `enable_if` и` is_base_of` - PullRequest
2 голосов
/ 08 июня 2019

Я создаю operator+, используя функцию шаблона для иерархии типов. Первая простая попытка работала нормально, пока я не попытался объединить строку в другой части кода, и GCC 8.3 попытался использовать мой operator+.

Попытка использовать enable_if и is_base_of, чтобы ограничить мою версию моими типами, чтобы SFINAE справился с этой проблемой.

Одна попытка среди многих:

template <  //
    typename T1, //
    typename T2, typename std::enable_if_t<std::is_base_of_v<LogicGateAbs, T2>, T2>       //
>
inline OrGate operator +(T1&& lhs, T2&& rhs) {
    return OrGate { LogicGateAbs::make_concrete(lhs), LogicGateAbs::make_concrete(rhs) };
}

Здесь компилятор правильно указывает типы:

./src2/test.cpp:90:11: error: no match for ‘operator+’ (operand types are ‘TrueGate’ and ‘TrueGate’)
     (True + True);
      ~~~~~^~~~~~

Но почему тогда он возвращает 'false' для is_base_of, поскольку LogicGateAbs является основой TrueGate?

../src2/test.cpp:83:15: note: candidate: ‘template<class T1, class T2, typename std::enable_if<is_base_of_v<LogicGateAbs, T2>, T2>::type <anonymous> > OrGate operator+(T1&&, T2&&)’
 inline OrGate operator +(T1&& lhs, T2&& rhs) {
               ^~~~~~~~
../src2/test.cpp:83:15: note:   template argument deduction/substitution failed:
../src2/test.cpp:81:32: error: no type named ‘type’ in ‘struct std::enable_if<false, TrueGate&>’
     typename T2, typename std::enable_if_t<std::is_base_of_v<LogicGateAbs, T2>, T2>       //
                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Перебрал каждую его версию в StackOverflow, не заставляя работать версию.

Базовая функция работает по мере необходимости. Ссылка на rvalue необходима, поскольку входные данные часто являются временными значениями.

Ответы [ 3 ]

1 голос
/ 08 июня 2019

Вам нужно ::std::remove_reference, да. Но вам также необходимо проверить типы обоих аргументов. : - /

template <typename T1, typename T2>
inline typename
::std::enable_if<
    ::std::is_base_of_v<LogicGateAbs, typename std::remove_reference_t<T1>> &&
    ::std::is_base_of_v<LogicGateAbs, typename std::remove_reference_t<T2>>,
    AndGate>::type
operator *(T1 &&lhs, T2 &&rhs) {
    using NRT1 = typename ::std::remove_reference<T1>::type;
    using NRT2 = typename ::std::remove_reference<T2>::type;
    AndGate gate { ::std::make_unique<NRT1>(::std::move(lhs)), ::std::make_unique<NRT2>(::std::move(rhs)) };
    return gate;
}

Теперь это не будет совпадать, если любая из сторон выражения не получена из LogicGateAbs. У меня действительно плохое предчувствие не-шаблонной версии. Эти только работают, если аргументы являются временными. Единственная причина, по которой он работает для TrueGate, заключается в том, что конструктор перемещения для него эквивалентен его конструктору копирования. Конструкторы ходов для вещей с unique_ptr членами являются разрушительными ходами.

1 голос
/ 08 июня 2019

Обратите внимание на TrueGate& в сообщении об ошибке: это T2, выведенное как таковое, поскольку аргумент является lvalue. std:is_base_of требует фактических типов классов, поэтому используйте std::remove_reference_t.

0 голосов
/ 08 июня 2019

Требуется окончательная версия std::remove_reference_t и =, предоставляемые @Davis и `@Sam.Слава обоим.

В LogicalGateAbs:

class LogicalGateAbs {
...
    template <typename T>
    static inline auto make_concrete(T&& rhs) {
        using NRT = typename std::remove_reference<T>::type;
        return std::make_unique < NRT > (std::move(rhs));
    }
};

И версия operator+:

//----------------------------------------------------------------------
template <typename T>
using check_type = std::enable_if_t<std::is_base_of_v<LogicGateAbs, typename std::remove_reference_t<T>>, T>;
//-----------------------------------------------------------------------
template <typename T1, typename = check_type<T1>, typename T2, typename = check_type<T2> >
inline OrGate operator +(T1&& lhs, T2&& rhs) {
    return OrGate { "op+", LogicGateAbs::make_concrete(lhs), LogicGateAbs::make_concrete(rhs) };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...