问题
I'm creating an operator+
using a template function for a type hierarchy. First simple attempt worked fine until I tried to concatenate a string in another part of the code and GCC 8.3 tried to use my operator+
.
Trying to use enable_if
and is_base_of
to restrict my version to my types so SFINAE handles the issue.
One attempt among many is:
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) };
}
Here the compiler gives the types correctly:
./src2/test.cpp:90:11: error: no match for ‘operator+’ (operand types are ‘TrueGate’ and ‘TrueGate’)
(True + True);
~~~~~^~~~~~
But why does it then return 'false' for is_base_of
since LogicGateAbs
is the base of 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> //
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Gone through every version of this on StackOverflow without getting a version to work.
The base function works as needed. The rvalue reference is necessary for the inputs are often temporary values.
回答1:
Notice the TrueGate&
in the error message: that’s T2
, deduced as such because the argument is an lvalue. std:is_base_of
requires actual class types, so use std::remove_reference_t
.
回答2:
You need ::std::remove_reference
, yes. But you also need to check the types of both arguments. :-/
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;
}
Now it won't match if either side of the expression isn't derived from LogicGateAbs
. I actually have a really bad feeling about the non-template version. These only work if the arguments are temporaries. The only reason it's working for TrueGate
is that the move constructor for it is equivalent to its copy constructor. The move constructors for the things with unique_ptr
members are destructive moves.
回答3:
The final version required std::remove_reference_t
and the =
supplied by @Davis and `@Sam. Kudos to both.
In 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));
}
};
And the version of 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) };
}
来源:https://stackoverflow.com/questions/56502143/cant-restrict-overload-of-template-function-operator-to-my-class-hierarchy-u