Making sfinae works for functions with deduced return type?

六月ゝ 毕业季﹏ 提交于 2019-12-24 05:04:45

问题


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.


回答1:


SFINAE can only catch substitution errors.

Some errors when calling a function cannot be substitution errors. These include errors that happen when you parse the body of a function.

C++ explicitly chose to exclude these errors from triggering SFINAE and instead trigger hard errors to free compilers from having to compile bodies of arbitrary functions to determine if SFINAE occurs. As SFINAE must be done during overload resolution, this makes the overload resolution code of a C++ compiler easier.

If you want your code to be SFINAE friendly, you cannot use lambdas like g or h.




回答2:


Your SFINAE works just fine, as regards to apply.

As to why f works, it's because:

  1. It's a template and

  2. It fails already in the substitution phase.

This means it has SFINAE of it's own, and for f the default do-nothing is called (for array), as intended.


Would there be a smart way in C++14 to modify the apply utility so that it forces...

No, apply already does all it should.

As to why g() and h() does not work, they will produce hard errors when you call them with something that does not have capacity. A utility function you (try to) apply after that can not remove that hard error. The only way to fix this is "double whammy" SFINAE, like you do in f()



来源:https://stackoverflow.com/questions/43034729/making-sfinae-works-for-functions-with-deduced-return-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!