推导返回类型模板 C++

Posted

技术标签:

【中文标题】推导返回类型模板 C++【英文标题】:deducing return type template c++ 【发布时间】:2022-01-11 15:41:53 【问题描述】:

我正在用 C++ 制作解析器组合器。目前,我正在尝试创建一个序列函数,该函数将按顺序调用其他解析器并将所有结果返回一个元组。

代码

template<typename T>
T parse(std::function<T(Stream*)> parser, Stream* stream)

    return parser(stream);


std::function<char(Stream*)> Char(char c)

    return [=](Stream* stream) -> char 
        auto ch = stream->Next();
        if (ch == c) 
            stream->Consume(1);
            return c;
        
        return 0;
    ;



template <typename ...T, typename R>
std::function<R(Stream*)> Seq(T... a)

    return [=](Stream* stream) -> R 
        std::tuple tuple =  a(stream)... ;
        return tuple;
    ;



问题是函数 Seq 需要知道返回类型是一个元组。该元组由参数包内函数的所有结果组成。所以我不知道如何告诉编译器返回类型是什么。

这是主要代码。

auto& charA = Char('a');
auto& seq = Seq(Char('#'), charA);

Stream stream("text.txt");

parse(seq, &stream);

这是错误跟踪。

TestLayer.cpp(338,27): error C3547: el parámetro de plantilla 'R' no se puede usar porque sigue a un paquete de parámetros de plantilla y no se puede deducir de los parámetros de funciones de 'Seq'
TestLayer.cpp(337): message : vea la declaración de 'R'
TestLayer.cpp(356,14): error C2672: 'Seq': no se encontró una función sobrecargada que coincida
TestLayer.cpp(356,14): error C2783: 'std::function<R(Stream *)> Seq(T...)': no se pudo deducir el argumento de plantilla para 'R'
TestLayer.cpp(338): message : vea la declaración de 'Seq'
TestLayer.cpp(356,12): error C2530: 'seq': se deben inicializar las referencias
TestLayer.cpp(360,11): error C3536: 'seq': no se puede usar antes de inicializarse
TestLayer.cpp(360,2): error C2672: 'parse': no se encontró una función sobrecargada que coincida
TestLayer.cpp(360,20): error C2784: 'T parse(std::function<T(Stream *)>,Stream *)': no se pudo deducir el argumento de plantilla para 'std::function<T(Stream *)>' desde 'int'
TestLayer.cpp(293): message : vea la declaración de 'parse'

编辑:

所以,感谢 cmets,我想出了这个解决方案:

template <typename ...T>
auto Seq(T... a) -> std::function<decltype(std::tuple a(std::declval<Stream*>())... )(Stream*)>

    return [=](Stream* stream) -> decltype(std::tuple a(std::declval<Stream*>())... ) 
        std::tuple tuple =  a(stream)... ;

        return tuple;
    ;


不知道是不是最优解。

【问题讨论】:

尾随返回类型加上decltype() 可能是最简单的一个:auto Seq(.....) -&gt; decltype(std::tuplea(nullptr)...)。如果您需要真正的Stream* 而不是 nullptr, use std::declval` 在declval 中创建一个@ 第一个错误消息似乎说您无法推断出R,因为它遵循参数包。 你试过自动返回类型吗?从 c++14 开始就可以使用。 非常感谢@yeputons 的评论。也许解决方案比我最初想要的更复杂。我愿意听取其他解决方案。 在这种情况下仅使用 auto 不起作用。但是感谢@TanveerBadar 【参考方案1】:

在 C++11 中,您可以找出 std::functions R 参数应该在不同的位置。我已选择将其设为具有默认类型的模板参数:

template<
    typename... T,
    class R = decltype(std::make_tuple(std::declval<T>()(std::declval<Stream*>())...))
>
auto Seq(T... a) -> std::function<R(Stream*)>

    return [=](Stream* stream) 
        return std::make_tuple(a(stream)...);
    ;

这是一个没有额外模板参数的替代版本:

template<typename... T>
auto Seq(T... a) ->
    std::function<
        decltype(
            std::make_tuple(std::declval<T>()(std::declval<Stream*>())...)
        )(Stream*)
    >

    return [=](Stream* stream) 
        return std::make_tuple(a(stream)...);
    ;

在 C++17 中更简单:

template<typename... T>
auto Seq(T... a) 
    return std::function([=](Stream* stream) 
        return std::make_tuple(a(stream)...);
    );

【讨论】:

以上是关于推导返回类型模板 C++的主要内容,如果未能解决你的问题,请参考以下文章

参数和返回类型中的模板类型推导

C++ 混合强类型基类与 CRTP 和返回值类型推导

从std :: function中推导返回和参数类型作为模板函数参数传递?

第3课 类型推导_追踪返回类型

现代C++笔记

简化代码,提高效率:C++ auto关键字的魅力