Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiating a function definition 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 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 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?

like image 740
psosera Avatar asked Mar 31 '11 17:03

psosera


2 Answers

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

template <typename T>
auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) {
    return ++*arg.t;
}
like image 156
James McNellis Avatar answered Sep 30 '22 21:09

James McNellis


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);
}
like image 23
Johannes Schaub - litb Avatar answered Sep 30 '22 19:09

Johannes Schaub - litb