As an exercise in understanding C++0x, I am trying to create a C++ class that wraps a pointer of some template-ized type:
template <typename T>
class Wrapper {
T *t;
/* ... */
};
Inside of the Wrapper class, I would like to expose any overloaded operators that T may implement through the Wrapper class. The wrapper itself simply forwards the function call to the underlying t object.
template <typename U>
auto operator+(U &u) -> decltype (*t + u) {
return *t + u;
}
The catch is that I do not want Wrapper exposing operators that T may not implement. For example, if T does not implement operator+ then Wrapper should not expose operator+ as well.
In the case of operator+ (and any binary operation), everything works out because the operator necessarily becomes a template 开发者_JAVA技巧function and is thus only instantiated when we try to invoke, e.g., Wrapper::operator+.
However, in the case of unary operators (e.g., ++), there is not a clear way to guard the operator so that it is instantiated iff T implements operator++. For example, the naive implementation of operator++ in this class
auto operator++() -> decltype(++(*t)) {
return ++(*t);
}
fails to compile for a T that 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 instantiate Wrapper and the declaration of Wrapper::operator++() but not its definition unless we invoke it (or explicitly instantiate it). Normally this would be ok, because the use of X::operator++ occurs only in the definition of Wrapper::operator++(). However, because of decltype, we use X::operator++ in the declaration so that the typechecker checks for the existence of X::operator++ and thus fails.
Can we define operator++() (and in general any such forwarding function that uses decltype) with the property that it is instantiated iff the underlying object also supports operator++()? Or given the semantics of template instantiation along with decltype, is this impossible to accomplish?
You can declare the operator as a non-member template:
template <typename T>
auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) {
return ++*arg.t;
}
You could also do tricks with default template arguments, just for making the operand of the operator be dependent
template<typename Trick = T>
auto operator++() -> decltype(++(static_cast<Trick&>(*t))) {
return ++(*t);
}
Perhaps with a helper function in 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);
}
If you can figure out how to work std::enable_if
into the operator signature, here's a metafunction that checks for the existence of e.g. 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, it didn't like
has_deref_opr_sfinae_impl_helper
being inside ofhas_deref_opr
, not sure why. Maybe this is changed in more recent versions of GCC - VC++ 2010 SP1 fails to compile this due to a template instantiation bug that I couldn't find a workaround for :-[
Hope this helps.
精彩评论