Creating an instance of a function in a template that uses decltype only in certain circumstances

As an exercise in understanding C ++ 0x, I am trying to create a C ++ class that wraps a pointer to some type of template:

template <typename T> class Wrapper { T *t; /* ... */ }; 

Inside the Wrapper class, I would like to show any overloaded operators that T can implement through the Wrapper class. The shell itself simply redirects the function call to the main object t.

 template <typename U> auto operator+(U &u) -> decltype (*t + u) { return *t + u; } 

The trap is that I do not want Wrapper to expose operators that T cannot implement. For example, if T does not implement the + operator, then Wrapper should also not expose the + operator.

In the case of the + operator (and any binary operation), everything works because the operator necessarily becomes a template function and therefore is created only when you try to call, for example, Wrapper :: operator +.

However, in the case of unary operators (e.g. ++), there is no clear way to protect the operator so that it is instantiated if T implements the ++ operator. For example, a naive implementation of the ++ operator in this class

 auto operator++() -> decltype(++(*t)) { return ++(*t); } 

cannot compile for T which does not support ++ () operator.

From my understanding of the standard, if we have the following code that uses Wrapper

 class X { }; Wrapper<X> w; 

We will create an instance of Wrapper and declare Wrapper :: operator ++ (), but not define it unless we call it (or explicitly create an instance). This would normally be normal, because the use of X :: operator ++ only occurs in the definition of Wrapper :: operator ++ (). However, due to decltype, we use X :: operator ++ in the declaration, so typechecker checks for the existence of X :: operator ++ and thus fails.

Is it possible to define a ++ () operator (and generally any such forwarding function that uses decltype) with the property that it is created if the base object also supports the ++ () operator? Or, given the semantics of creating a template along with decltype, is it impossible to execute?

+6
c ++ c ++ 11 templates decltype
source share
3 answers

You can declare an operator as a non-member template:

 template <typename T> auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) { return ++*arg.t; } 
+5
source share

You can also do tricks with default template arguments, just to make the operand of the operator dependent

 template<typename Trick = T> auto operator++() -> decltype(++(static_cast<Trick&>(*t))) { return ++(*t); } 

Perhaps with an auxiliary function between

 template<typename /* Ignored */, typename T> T &&id(T &&t) { return std::forward<T>(t); } template<typename Void = void> auto operator++() -> decltype(++(*id<Void>(t))) { return ++(*t); } 
+4
source share

If you can figure out how std::enable_if works in the signature of an operator, a metafunction is used here that checks for the existence of, for example, operator-> :

 #include <type_traits> template<typename T, typename R> inline R* has_deref_opr_sfinae_impl_helper(R (T::*)()) { return 0; } template<typename T, typename R> inline R* has_deref_opr_sfinae_impl_helper(R (T::*)() const) { return 0; } template< typename T, bool IsPointer = std::is_pointer<T>::value && !std::is_same< typename std::remove_cv< typename std::remove_pointer< typename std::remove_cv<T>::type >::type >::type, void >::value > class has_deref_opr { template< typename U, typename R = decltype(has_deref_opr_sfinae_impl_helper(&U::operator->)) > struct sfinae_impl { }; typedef char true_t; struct false_t { true_t f[2]; }; template<typename U> static true_t check(U*, sfinae_impl<U>* = 0); template<typename U> static false_t check(...); public: static bool const value = sizeof(check<T>(0)) == sizeof(true_t); }; template<typename T> class has_deref_opr<T, true> { public: static bool const value = true; }; 

A few notes:

  • I tested with GC 4.4.1, he didn’t like has_deref_opr_sfinae_impl_helper inside has_deref_opr , you don’t know why. Perhaps this has changed in later versions of GCC
  • VC ++ 2010 SP1 failed to compile this due to an error creating the template instance, that I could not find a workaround for: - [

Hope this helps.

+2
source share

All Articles