使用std :: result_of意外SFINAE失败

时间:2015-02-18 16:33:48

标签: c++ c++14 sfinae result-of

在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 个答案:

答案 0 :(得分:9)

这是由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); }

缺少 trailing-return-type 意味着两件事:

  1. 他们不再“完全回归”;相反,使用auto的规则推导出返回类型,从而导致它被腐朽。尾部返回类型,当其中的表达式格式正确时,等同于返回decltype(auto)
  2. SFINAE不再适用于_VSTD::forward<_T1>(__t) + _VSTD::forward<_T2>(__u)这个词。在无错误的实现中,operator()声明将从重载集中删除,重载解析将失败,然后std::result_of将执行其SFINAE友好的魔法。相反,函数声明被成功实例化,由重载决策选择,然后当编译器尝试实例化实体以实际推断返回类型时发生硬错误。
  3. 您的问题是由#2引起的。