无法使用`enable_if`和`is_base_of`将模板函数`operator +`的重载限制为我的类层次结构

时间:2019-06-07 23:15:44

标签: c++ templates operator-overloading

我正在使用模板函数为类型层次结构创建operator+。第一次简单的尝试就可以正常工作,直到我尝试在代码的另一部分中连接字符串,并且GCC 8.3尝试使用我的operator+

尝试使用enable_ifis_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);
      ~~~~~^~~~~~

但是为什么由于is_base_ofLogicGateAbs的基数而又为TrueGate返回'false'?

../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上浏览了此版本的每个版本,而没有一个版本可以正常工作。

基本功能根据需要工作。右值引用是必需的,因为输入通常是临时值。

3 个答案:

答案 0 :(得分:1)

请注意错误消息中的TrueGate&T2,其推论是因为该参数是一个左值。 std:is_base_of需要实际的类类型,因此请使用std::remove_reference_t

答案 1 :(得分:1)

您需要::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派生的,它将不匹配。对于非模板版本,我实际上有一种非常不好的感觉。如果参数是临时变量,则这些 only 起作用。它适用于TrueGate的唯一原因是它的move构造函数等同于其copy构造函数。具有unique_ptr成员的事物的move构造函数是破坏性的动作。

答案 2 :(得分:0)

最终版本需要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) };
}