使sfinae适用于推导出返回类型的函数?

时间:2017-03-26 21:26:27

标签: c++ lambda c++14 template-meta-programming sfinae

请考虑以下代码:

// -------------------------------------------------------------------------- //
// 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;
}
// -------------------------------------------------------------------------- //

实用程序apply在可以编译时将参数应用于参数,否则它什么都不做。该机制依赖于sfinae。但是,对于从主体推导出返回类型的函数,如上例中的gh,sfinae会失败。在C ++ 14中是否有一种聪明的方法可以修改apply实用程序,这样即使对于从正文中推断出返回类型的函数,它也会强制执行sfinae?

注意:我希望将gh视为f,这意味着apply的调用应该调用void版本。< / p>

2 个答案:

答案 0 :(得分:6)

SFINAE只能捕获替代错误。

调用函数时的一些错误不能是替换错误。这些包括解析函数体时发生的错误。

C ++明确选择将这些错误排除在触发SFINAE之外,而是触发硬错误,使编译器不必编译任意函数体来确定是否发生了SFINAE。由于SFINAE必须在重载解析期间完成,这使得C ++编译器的重载解析代码更容易。

如果您希望代码对SFINAE友好,则不能使用gh等lambda。

答案 1 :(得分:1)

对于apply,您的SFINAE工作得很好。

至于为什么f有效,这是因为:

  1. 这是一个模板,

  2. 它已在替换阶段失败。

  3. 这意味着它拥有自己的SFINAE,以及f 默认的do-nothing被调用(对于array),如预期的那样。

      

    在C ++ 14中是否有一种聪明的方法来修改apply   实用程序,以便它... ...

    不,apply已经做到了所有这一切。

    至于为什么g()h()不起作用,他们会产生 当你用没有的东西调用它们时会出现硬错误 有capacity。 一个实用功能你(尝试)在之后应用即可 不删除那个硬错误。 解决这个问题的唯一方法是“双重打击”SFINAE, 就像你在f()

    中所做的那样