Consider the following code:
// -------------------------------------------------------------------------- //
// Preprocessor
#include <array>
#include <vector>
#include <utility>
#include <iostream>
#include <type_traits>
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// Calls a function without arguments if it can be called
template <
class F,
class... Args,
class = decltype(std::declval<F>()())
>
decltype(auto) apply(F&& f)
{
std::cout<<"apply(F&& f)"<<std::endl;
return std::forward<F>(f)();
}
// Calls a function with arguments if it can be called
template <
class F,
class Arg,
class... Args,
class = decltype(std::declval<F>()(
std::declval<Arg>(), std::declval<Args>()...
))
>
decltype(auto) apply(F&& f, Arg&& arg, Args&&... args)
{
std::cout<<"apply(F&& f, Arg&& arg, Args&&... args)"<<std::endl;
return std::forward<F>(f)(
std::forward<Arg>(arg),
std::forward<Args>(args)...
);
}
// Does nothing if the function cannot be called with the given arguments
template <
class F,
class... Args
>
void apply(F&& f, Args&&... args)
{
std::cout<<"apply(F&& f, Args&&... args)"<<std::endl;
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// Main function
int main(int argc, char* argv[])
{
// Initialization
auto f = [](auto&& x) -> decltype(std::forward<decltype(x)>(x).capacity()) {
return std::forward<decltype(x)>(x).capacity();
};
auto g = [](auto&& x) -> decltype(auto) {
return std::forward<decltype(x)>(x).capacity();
};
auto h = [](auto&& x) {
return std::forward<decltype(x)>(x).capacity();
};
// Test
apply(f, std::vector<double>()); // -> sfinae works
apply(g, std::vector<double>()); // -> sfinae works
apply(h, std::vector<double>()); // -> sfinae works
apply(f, std::array<double, 1>());// -> sfinae works
//apply(g, std::array<double, 1>()); -> sfinae fails, does not compile
//apply(h, std::array<double, 1>()); -> sfinae fails, does not compile
// Return
return 0;
}
// -------------------------------------------------------------------------- //
The utility apply
, applies a function to arguments when it can compile, otherwise it does nothing. The mechanism relies on sfinae. However, for function whose return type is deduced from the body, like g
and h
in the above example, sfinae fails. Would there be a smart way in C++14 to modify the apply
utility so that it forces sfinae to kicks in even for functions whose return type is deduced from the body?
Note: I would like to make g
and h
work as f
, meaning that the call of apply
should call the void
version.