How to handle void decltype ();

I would like to create a template that calls a member function on another object that returns the same type as the member function. The syntax for using decltype in a member function is a little ugly, but it seems to work in every case except one.

Here's the call:

struct container: map<string, C> 
{
    template< typename MemFnPtrType, typename... _ArgTypes>
    auto safeOperation(string key, MemFnPtrType mfp, _ArgTypes&&... args )
        -> decltype( (((C*)nullptr)->*mfp)(args...))
    {
        C* pC = NULL;
        decltype((pC->*mfp)(args...)) result;

        iterator it = find(key);
        if (it != end())
        {
            C* pC = &(it->second);
            result = (pC->*mfp)(args...);
            cout << "result:" << result << "\n";
        }
        else
        {
            cout << "key: " << key << " missing\n";
        }
        return result;
    }
};

      

This works fine as long as the member function doesn't return void.

Is there a way to detect this and leave it out of line?

I could create a voidSafeOperation function. I don't mind creating a different template, but I would like to use the same name "safeOperation", so call sites don't need to use different helpers based on the return type of the member function.

Thank!

Complete example: http://cpp.sh/7ft

+4


source to share


2 answers


Unfortunately I think you are stuck here having SFINAE on return type. Start with a type trait (it's much easier than an expression decltype

)

template <typename MF, typename... Args>
using Res = typename std::result_of<MF(C, Args...)>::type;

      

And then just switch:

template <typename MF, typename... Args>
typename std::enable_if<
    std::is_same<Res<MF, Args...>, void>::value
>::type safeOperation(string key, MF mfp, Args... args)
{
     /* void case */
}

template <typename MF, typename... Args>
typename std::enable_if<
    !std::is_same<Res<MF, Args...>, void>::value,
    Res<MF, Args...>
>::type safeOperation(string key, MF mfp, Args... args)
{
     /* non-void case */
}

      



Or you can mark the shipment with is_void

:

template <typename MF, typename... Args>
Res<MF, Args...> safeOperation(string key, MF mfp, Args... args)
{
    return safeOperation(std::is_void<Res<MF, Args...>>{},
                         key, mfp, args...);
}

      

from:

template <typename MF, typename... Args>
void safeOperation(std::true_type /* void */, 
                   string key, MF mfp, Args... args) 
{ .. }

template <typename MF, typename... Args>
Res<MF, Args...> safeOperation(std::false_type /* non-void */, 
                               string key, MF mfp, Args... args) 
{ .. }

      

+9


source


This is the angular case. auto

c decltype

for the final return type does not work for the return type void

because it void

is an incomplete type.

example:



auto nothingMuch()
{
    return;
}

// nope
//decltype(void {}) nothingMuchEither()
//{
//  return;
//}

// nope
//auto noTrailing() -> decltype(void {})
//{
//
//}

// always works
decltype(auto) nothing()
{
    return;
}

auto main() -> decltype(int {})
{
    nothing();
    nothingMuch();
    return 0;
}

      

The simplest solution is to replace auto

and decltype

at the end of the return type decltype(auto)

with as the return type (requires C ++ 14).

0


source







All Articles