代码之家  ›  专栏  ›  技术社区  ›  r3mus n0x

使用std::invoke_result_t和泛型lambda时出现硬错误

  •  3
  • r3mus n0x  · 技术社区  · 7 年前

    我有一个类似容器的类,其方法与 std::apply const std::invoke_result_t std::调用结果

    #include <type_traits>
    #include <utility>
    
    template <typename T>
    class Container
    {
    public:
        template <typename F>
        std::invoke_result_t<F, T &> apply(F &&f)
        {
            T dummyValue;
            return std::forward<F>(f)(dummyValue);
        }
    
        template <typename F>
        std::invoke_result_t<F, const T &> apply(F &&f) const
        {
            const T dummyValue;
            return std::forward<F>(f)(dummyValue);
        }
    };
    
    int main()
    {
        Container<int> c;
        c.apply([](auto &&value) {
            ++value;
        });
        return 0;
    }
    

    使用Clang 6.0编译时出现错误消息:

    main.cc:27:9: error: cannot assign to variable 'value' with const-qualified type 'const int &&'
            ++value;
            ^ ~~~~~
    type_traits:2428:7: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const int &>' requested here
          std::declval<_Fn>()(std::declval<_Args>()...)
          ^
    type_traits:2439:24: note: while substituting deduced template arguments into function template '_S_test' [with _Fn = (lambda at main.cc:26:13), _Args = (no value)]
          typedef decltype(_S_test<_Functor, _ArgTypes...>(0)) type;
                           ^
    type_traits:2445:14: note: in instantiation of template class 'std::__result_of_impl<false, false, (lambda at main.cc:26:13), const int &>' requested here
        : public __result_of_impl<
                 ^
    type_traits:2831:14: note: in instantiation of template class 'std::__invoke_result<(lambda at main.cc:26:13), const int &>' requested here
        : public __invoke_result<_Functor, _ArgTypes...>
                 ^
    type_traits:2836:5: note: in instantiation of template class 'std::invoke_result<(lambda at main.cc:26:13), const int &>' requested here
        using invoke_result_t = typename invoke_result<_Fn, _Args...>::type;
        ^
    main.cc:16:10: note: in instantiation of template type alias 'invoke_result_t' requested here
        std::invoke_result_t<F, const T &> apply(F &&f) const
             ^
    main.cc:26:7: note: while substituting deduced template arguments into function template 'apply' [with F = (lambda at main.cc:26:13)]
        c.apply([](auto &&value) {
          ^
    main.cc:26:23: note: variable 'value' declared const here
        c.apply([](auto &&value) {
                   ~~~~~~~^~~~~
    

    我不确定是否 std::调用结果 SFINAE是否友好,但我不认为这是问题所在,因为我已尝试将其替换为尾随返回类型,例如:

    auto apply(F &&f) const -> decltype(std::declval<F>()(std::declval<const T &>()))
    

    得到了一个类似的错误:

    main.cc:27:9: error: cannot assign to variable 'value' with const-qualified type 'const int &&'
            ++value;
            ^ ~~~~~
    main.cc:16:41: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const int &>' requested here
        auto apply(F &&f) const -> decltype(std::declval<F>()(std::declval<const T &>()))
                                            ^
    main.cc:26:7: note: while substituting deduced template arguments into function template 'apply' [with F = (lambda at main.cc:26:13)]
        c.apply([](auto &&value) {
          ^
    main.cc:26:23: note: variable 'value' declared const here
        c.apply([](auto &&value) {
                   ~~~~~~~^~~~~
    

    问题:

    1. 我如何解决这个问题?
    2 回复  |  直到 7 年前
        1
  •  5
  •   Brian Bi    7 年前

    lambda已推导出返回类型,除非您显式指定返回类型。因此 std::invoke_result_t 必须实例化主体才能确定返回类型。此实例化不在即时上下文中,会导致硬错误。

    [](auto &&value) -> void { /* ... */ }
    

    在这里,lambda的主体在 apply

        2
  •  5
  •   Yakk - Adam Nevraumont    7 年前

    所以这里的重载解析有点愚蠢。

    const apply 工作,我永远不会打电话 const apply

    F

    template <typename F>
    std::invoke_result_t<F, T &> apply(F &&f)
    
    template <typename F>
    std::invoke_result_t<F, const T &> apply(F &&f) const
    

    现在,当传递lambda类型的 F 对这些?

    auto 返回类型。为了在传递某物时找出实际的返回类型,编译器必须检查lambda的主体。

    我们可以通过以下方法避免实例化lambda的主体:

    [](auto &&value) -> void { /* ... */ }
    

    这样做之后,两个 :

    模板<typename F>
    std::invoke_result_t<F、 T&&燃气轮机;适用(F&&(f)
    
    std::invoke_result_t<F、 康斯特&燃气轮机;适用(F&&f) 常数
    

    void

    template <typename F=$lambda$>
    void apply(F &&f)
    
    template <typename F=$lambda$>
    void apply(F &&f) const
    

    现在,请注意 apply const 仍然存在。如果你打电话 ,您将得到实例化该lambda主体所导致的硬错误。

    如果希望lambda本身对SFINAE友好,则需要执行以下操作:

    #define RETURNS(...) \
      noexcept(noexcept(__VA_ARGS__)) \
      -> decltype(__VA_ARGS__) \
      { return __VA_ARGS__; }
    
    [](auto &&value) RETURNS(++value)
    

    请注意,此lambda略有不同,因为它返回对值的引用。我们可以通过以下方式避免这种情况:

    [](auto &&value) RETURNS((void)++value)
    

    现在lambda对SFINAE都很友好 具有与原始lambda相同的行为 您的原始程序按照此更改进行编译。

    应用现在被SFINAE从重载解析中删除。这使得它反过来又是友好的。

    有人提议采取行动 RETURNS 并将其重命名 => ,但上次我检查时,它没有被接受 .

    推荐文章