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
source to share
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)
{ .. }
source to share
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).
source to share