Search code examples
c++c++11generic-programmingdecltypeargument-dependent-lookup

Determining return type of "generic function"


Suppose, I want to develop a generic library which should be usable with number-like types including double and user-defined types. The problem, I'm facing right now is that I don't know how to write the return type of a function template much like this one:

template<class T>
auto transmogrify(T x)
-> ???
{
    using std::abs;
    return abs(x)+2.0;
}

The using declaration makes this function template's body work for primitive types because these don't have an associated namespace (and hence, there is no ADL). But I want transmogrify to use specialized abs functions in case the author of a user-defined type provides his own abs function. I can't simply use

-> decltype( abs(x)+2.0 )

because this would not work for, say, doubles since std::abs is not in scope (as far as I can tell). But writing

-> decltype( std::abs(x)+2.0 )

would disable ADL. But disabling ADL is not an option. Also, the value returned by a specialized abs function might not be of type T but some other type.

Any ideas on how to solve the return type issue while (a) keeping ADL and (b) falling back on some default function (like std::abs in this case) for types that don't provide a specialized abs.


Solution

  • Use a separate namespace, where you can put the using clause. This prevents the namespace pollution, since the using clause only applies to that namespace. I would recommend naming it something unique, so you don't accidentally spread it around.

    namespace transmog_detail
    {
       using std::abs;
    
       template<class T>
       auto transmogrify(T x) -> decltype(abs(x) + 2.0)
       {
          return abs(x) + 2.0;
       }
    }
    
    // Then pull it into the current namespace, as recommended by @LucDanton.
    using transmog_detail::transmogrify;
    
    // Or if there is a reason, you can forward. 
    // template<class T>
    // auto transmogrify(T x)
    // -> decltype(transmog_detail::transmogrify(x))
    // {
    //    return transmog_detail::transmogrify(x);
    // }