In an attempt to write a wrapper type for another type T I ran into a rather unpleasant problem: I would like to define some binary operators (such as + ) that forward any wrapper operation to the base type, but I need these operators to accept any of the possible combinations that include wrapper :
wrapper() + wrapper() wrapper() + T() T() + wrapper()
A naive approach involves recording all potential overloads directly.
But I donโt like writing duplicate code and need a little more complicated, so I decided to implement it using a very general template and limit the possible types with enable_if .
My attempt is shown at the bottom of the question (sorry, this is the minimum as I can imagine). The problem is that it will work in an infinite recursion error:
- To evaluate
test() + test() , the compiler looks at all potential overloads. - The operator defined here is actually a potential overload, so it is trying to build a return type.
- The return type has an
enable_if clause that should prevent it from actually overloading, but the compiler simply ignores this and tries to calculate decltype first, which requires ... - ... instance
operator+(test, test) .
And we returned to where we started. GCC is good enough to spit out an error; Clang is just segfaults.
What would be a good, clean solution for this? (Keep in mind that there are other operators that need to follow the same pattern.)
template<class T> struct wrapper { T t; }; // Checks if the type is instantiated from the wrapper template<class> struct is_wrapper : false_type {}; template<class T> struct is_wrapper<wrapper<T> > : true_type {}; // Returns the underlying object template<class T> const T& base(const T& t) { return t; } template<class T> const T& base(const wrapper<T>& w) { return wt; } // Operator template<class W, class X> typename enable_if< is_wrapper<W>::value || is_wrapper<X>::value, decltype(base(declval<W>()) + base(declval<X>())) >::type operator+(const W& i, const X& j); // Test case struct test {}; int main() { test() + test(); return 0; }
Here is a rather clumsy solution that I would prefer not to use unless I want to:
// Force the evaluation to occur as a 2-step process template<class W, class X, class = void> struct plus_ret; template<class W, class X> struct plus_ret<W, X, typename enable_if< is_wrapper<W>::value || is_wrapper<X>::value>::type> { typedef decltype(base(declval<W>()) + base(declval<X>())) type; }; // Operator template<class W, class X> typename plus_ret<W, X>::type operator+(const W& i, const X& j);