代码之家  ›  专栏  ›  技术社区  ›  KentH

使用std::result_of的意外SFINAE失败

  •  2
  • KentH  · 技术社区  · 10 年前

    在c++14中,如果表达式格式不正确*,std::result_of应该导致SFINAE。相反,我在下面的最后一种情况下得到了一个编译错误(“二进制表达式的操作数无效”)(即让编译器推断std::plus的类型<>)。前三个案例按预期运行。代码和结果如下所示。

    #include <boost/mpl/placeholders.hpp>
    #include <boost/mpl/apply.hpp>
    #include <iostream>
    #include <utility>
    #include <stdexcept>
    #include <functional>
    
    namespace mpl = boost::mpl;
    
    
    template <typename OP, typename T, typename OP_T = typename mpl::apply<OP, T>::type>
    struct apply_if_ok: OP_T {
    
        template <typename...Args, typename R = std::result_of_t<OP_T(Args...)>>
        R operator()(Args&&...args) const {
            return static_cast<OP_T>(*this)(std::forward<Args>(args)...);
        }
        template <typename...Args>
        auto operator()(...) const {
            // throw std::runtime_error("Invalid arguments");
            return "Invalid arguments";
        }
    };
    
    
    int main() {
        using OP = std::plus<mpl::_>;
        int i = 3;
    
        auto n1 = apply_if_ok<OP, void>()(1, 2);
        std::cout << "plus (1, 2) = " << n1 << std::endl;
    
        auto n2 = apply_if_ok<OP, void>()(1, &i);
        std::cout << "plus (1, *) = " << n2 << std::endl;
    
        auto n3 = apply_if_ok<OP, int>()(&i, &i);
        std::cout << "plus (*, *) = " << n3 << std::endl;
    
        // auto n4 = apply_if_ok<OP, void>()(&i, &i);
        // std::cout << "plus (*, *) = " << n4 << std::endl;
    }
    

    输出:

    % c++ -std=c++1y -g -pedantic    sfinae_result_of.cc   -o sfinae_result_of
    ./sfinae_result_of
    plus (1, 2) = 3
    plus (1, *) = 0x7fff5e782a80
    plus (*, *) = Invalid arguments
    
    % c++ -v
    Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
    Target: x86_64-apple-darwin14.1.0
    Thread model: posix
    

    如果你能指出我做错了什么,我将不胜感激!

    谢谢

    • 从…起 cppreference.com 。我认为相关的标准参考是20.10.7.6,最后一个表条目的注释。
    1 回复  |  直到 10 年前
        1
  •  9
  •   T.C. Yksisarvinen    10 年前

    这是由 bug in libc++ 这是我几天前刚刚报道的。( 使现代化 :错误 has been fixed 行李箱中。)

    问题是,它们的“菱形函子”实现是不合规的。例如,他们实施了 std::plus<void>::operator() 如下所示:

    template <class _T1, class _T2>
    _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
    auto operator()(_T1&& __t, _T2&& __u) const
        { return _VSTD::forward<_T1>(__t) + _VSTD::forward<_T2>(__u); }
    

    什么时候应该

    template <class _T1, class _T2>
    _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
    auto operator()(_T1&& __t, _T2&& __u) const
        -> decltype(_VSTD::forward<_T1>(__t) + _VSTD::forward<_T2>(__u))
        { return _VSTD::forward<_T1>(__t) + _VSTD::forward<_T2>(__u); }
    

    失踪者 尾随返回类型 意思是两件事:

    1. 他们不再“完美回归”;相反,返回类型是使用 auto 基本上导致其腐烂。当后面的返回类型中的表达式格式正确时,它相当于返回 decltype(auto) .
    2. SFINAE不再适用于表达式 _VSTD::forward<_T1>(__t) + _VSTD::forward<_T2>(__u) 。在无错误的实现中 operator() 声明将从重载集中删除,重载解析将失败,并且 std::result_of 然后将发挥其SFINAE友好的魔力。相反,函数声明被成功实例化,并通过重载解析进行选择,然后当编译器试图实例化主体以实际推断返回类型时,会发生硬错误。

    您的问题是由#2引起的。