为什么只有 AppleClang 无法编译此代码并给出“推导的返回类型在定义之前不能使用”?

问题描述 投票:0回答:1

我正在尝试编写一个使用

std::visit
和递归 lambda 的跨平台库,但它仅在 macOS 上无法编译。错误部分最小化为以下代码:

// test.cpp
#include <iostream>
#include <variant>

using namespace std;

template <typename... Ts>
struct Overloaded : Ts... {
    using Ts::operator()...;
};

template <typename Fn>
struct YCombinator {
    Fn fn;
    explicit YCombinator(Fn &&fn) : fn(std::forward<Fn>(fn)) {}
    template <typename... Args>
    inline decltype(auto) operator()(Args &&...args) const {
        return fn(*this, std::forward<Args>(args)...);
    }
};

// Deduction guide, not needed if using C++20
template <typename Fn>
YCombinator(Fn &&fn) -> YCombinator<Fn>;

int main(void) {
    auto visitor = YCombinator(Overloaded{
        [&](const auto &v, const int &i) -> void {
            if (i) {
                cout << "i: " << i << endl;
                variant<char, int> b{char(i - 1)};
                visit(v, b);
            }
        },
        [&](const auto &v, const char &c) -> void {
            if (c) {
                cout << "c: " << int(c) << endl;
                variant<char, int> b{int(c) - 1};
                visit(v, b);
            }
        },
    });
    variant<char, int> a{char(10)};
    visit(visitor, a);
    return 0;
}

我在以下环境中测试了上面的代码:

  • Linux
    • 海湾合作委员会 14.2.1:
      g++ -std=c++20 test.cpp
    • 叮当18.1.8:
      clang++ -std=c++20 test.cpp
  • Windows
    • VS 2022 与 MSVC 14.40:
      cl.exe /EHsc /std:c++20 test.cpp
    • 叮当19.1.0:
      clang++.exe -std=c++20 test.cpp

全部编译运行成功。但在 macOS 下,AppleClang 16.0.0 和 clang 18.1(通过 homebrew llvm 安装)会出现以下错误:

test.cpp:31:17: error: function 'visit<const YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, std::variant<char, int> &, void>' with deduced return type cannot be used before it is defined
   31 |                 visit(v, b);
      |                 ^
test.cpp:17:16: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>>>' requested here
   17 |         return fn(*this, std::forward<Args>(args)...);
      |                ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/__type_traits/invoke.h:341:10: note: in instantiation of function template specialization 'YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>>::operator()<int &>' requested here
  341 | decltype(std::declval<_Fp>()(std::declval<_Args>()...))
      |          ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/__type_traits/invoke.h:351:19: note: while substituting deduced template arguments into function template '__invoke' [with _Fp = const YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, _Args = <int &>]
  351 |   static decltype(std::__invoke(std::declval<_XFp>(), std::declval<_XArgs>()...)) __try_call(int);
      |                   ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/__type_traits/invoke.h:357:28: note: while substituting deduced template arguments into function template '__try_call' [with _XFp = const YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, _XArgs =(no value)]
  357 |   using _Result = decltype(__try_call<_Fp, _Args...>(0));
      |                            ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/__type_traits/invoke.h:428:68: note: in instantiation of template class 'std::__invokable_r<void, const YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, int &>' requested here
  428 | struct _LIBCPP_TEMPLATE_VIS is_invocable : integral_constant<bool, __invokable<_Fn, _Args...>::value> {};
      |                                                                    ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/__type_traits/invoke.h:434:40: note: (skipping 28 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
  434 | inline constexpr bool is_invocable_v = is_invocable<_Fn, _Args...>::value;
      |                                        ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/variant:500:32: note: in instantiation of function template specialization 'std::__variant_detail::__visitation::__base::__make_fmatrix<std::__variant_detail::__visitation::__variant::__value_visitor<YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &> &&, std::__variant_detail::__base<std::__variant_detail::_Trait::_TriviallyAvailable, char, int> &>' requested here
  500 |     constexpr auto __fmatrix = __make_fmatrix<_Visitor&&, decltype(std::forward<_Vs>(__vs).__as_base())...>();
      |                                ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/variant:586:20: note: in instantiation of function template specialization 'std::__variant_detail::__visitation::__base::__visit_alt<std::__variant_detail::__visitation::__variant::__value_visitor<YCombinator<Overloaded<(lambdaat test.cpp:27:9), (lambda at test.cpp:34:9)>> &>, std::__variant_detail::__impl<char, int> &>' requested here
  586 |     return __base::__visit_alt(
      |                    ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/variant:598:12: note: in instantiation of function template specialization 'std::__variant_detail::__visitation::__variant::__visit_alt<std::__variant_detail::__visitation::__variant::__value_visitor<YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &>, std::variant<char, int> &>' requested here
  598 |     return __visit_alt(__make_value_visitor(std::forward<_Visitor>(__visitor)), std::forward<_Vs>(__vs)...);
      |            ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/variant:1561:21: note: in instantiation of function template specialization 'std::__variant_detail::__visitation::__variant::__visit_value<YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, std::variant<char, int> &>' requested here
 1561 |   return __variant::__visit_value(std::forward<_Visitor>(__visitor), std::forward<_Vs>(__vs)...);
      |                     ^
test.cpp:43:10: note: in instantiation of function template specialization 'std::visit<YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, std::variant<char, int> &, void>' requested here
   43 |     std::visit(visitor, a);
      |          ^
/usr/local/opt/llvm@18/bin/../include/c++/v1/variant:1558:1: note: 'visit<const YCombinator<Overloaded<(lambda at test.cpp:27:9), (lambda at test.cpp:34:9)>> &, std::variant<char, int> &, void>' declared here
 1558 | visit(_Visitor&& __visitor, _Vs&&... __vs) {
      | ^
1 error generated.

那么为什么会出现这个错误,有没有办法解决它(最好不要彻底改变代码模式)?

c++ macos compiler-errors
1个回答
0
投票

我猜 macOS 上的 clang 目前在返回类型推导方面存在一些问题。

根据@MarekR的发现,一种解决方法是手动为YCombinator扣除带有

type_traits
的类型,即将问题中的YCombinator替换为

template <typename Fn>
struct YCombinator {
    Fn fn;
    explicit YCombinator(Fn &&fn) : fn(std::forward<Fn>(fn)) {}
    template <typename... Args>
    inline invoke_result_t<Fn, YCombinator &, Args...>
    operator()(Args &&...args) const {
        return fn(*this, std::forward<Args>(args)...);
    }
};

然后代码将通过 macOS clang>=17 的编译。

© www.soinside.com 2019 - 2024. All rights reserved.