I am implementing a wrapper class that wraps some base class object. I want the wrapper to be as unnoticeable as possible and therefore, I have already overloaded the ->
operator to return a reference to the wrapped object in order to provide immediate access to the base-class's interface.
Additionally, I want my wrapper type to also implement all operators that the base-class implements and just delegates the respective operator calls to the wrapped object. However, since I want to be able to wrap any arbitrary base-class, I don't know a priori what operators are defined for the wrapped class.
My current approach consists of just defining all operators for my wrapper and using SFINAE to disable those implementations that are not covered by base-class implementations.
The implementation for the operator overload essentially always looks like this
auto operator <operator> (const Wrapper &lhs, const Wrapper &rhs) { return lhs.get() <operator> rhs.get(); }
auto operator <operator> (const Wrapper &lhs, const Base &rhs) { return lhs.get() <operator> rhs; }
auto operator <operator> (const Base &lhs, const Wrapper &rhs) { return lhs <operator> rhs.get(); }
where <operator>
is the respective operator. Since, I don't want to duplicate this code for all operators, I have defined some macros to create the definitions for me.
This works for most operators, but now I also want to support the various assignment operators (e.g. *=
). Here, the lhs
argument must not be const
as it is modified via the action of the operator.
I could just generate these separately, but I thought that there must be a way to simply make the lhs
argument const or non-const depending on a constexpr boolean (available as macro-parameter). Thus, I created a templated helper cond_add_const< T, bool >
that returns const T
if passed true
and T
if passed false
.
The problem now is, that the overloads that involve the Base
class as direct parameters fail to be resolved due to template argument deduction failure. The problem seems to be that in order to apply my conditional const, I essentially have the replace the respective type with cond_add_const< T, true >::type
and apparently everything to the left of ::
does not participate in template argument deduction.
This seems rather frustrating and it also feels like for my simple case there should be a possibility to circumvent this limitation (or choose an approach that does not require it), but I just can't come up with one (that does not involve duplicating code). Any ideas?
MWE illustrating my problem:
#include <type_traits>
template< typename T, bool is_const > struct cond_add_const {
using type = typename std::conditional< is_const, typename std::add_const< T >::type, typename std::remove_const< T >::type >::type;
};
template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
}
int main() {
myFunc(1, 2);
}
Compiling this snippet with g
results in
test.cpp: In function ‘int main()’:
test.cpp:11:13: error: no matching function for call to ‘myFunc(int, int)’
11 | myFunc(1, 2);
| ^
test.cpp:7:29: note: candidate: ‘template<class T> void myFunc(typename cond_add_const<T, true>::type, int)’
7 | template< typename T > void myFunc(typename cond_add_const< T, true >::type lhs, int rhs) {
| ^~~~~~
test.cpp:7:29: note: template argument deduction/substitution failed:
test.cpp:11:13: note: couldn’t deduce template parameter ‘T’
11 | myFunc(1, 2);
|
CodePudding user response:
You might turn your template functions in non-template friend
function of your class:
template <typename T>
struct Wrapper
{
// ...
friend void myFunc(typename cond_add_const<Wrapper<T>, true >::type lhs, T rhs)
{ /*...*/ }
};
There are some caveats:
- the
friend
function can only be found via "ADL" (So at least one parameter should be of theWrapper
type).