【问题标题】:Template type inference using std::views使用 std::views 进行模板类型推断
【发布时间】:2022-01-02 06:09:02
【问题描述】:

我来函数式编程有点晚了,并开始关注范围/视图。我正在使用 MSVC19 并为 C++ 20 进行编译。

我正在使用std::views::transform,编译器似乎并没有像我天真希望的那样推断类型。

这里有一个小例子,它简单地接受一个字符串向量并计算它们的长度:

#include <vector>
#include <iostream>
#include <ranges>

template<typename E>
auto length(const E& s)
{
    std::cout << "Templated length()\n";
    return static_cast<int>(s.length());
}

template<typename E>
auto getLengths(const std::vector<E>& v)
{
    return  v | std::views::transform(length<E>);
}

int main()
{
    std::vector<std::string> vec = { "Larry","Curly","Moe" };
    for (int i : getLengths(vec))
    {
        std::cout << i << "\n";
    }
    return 0;
}

输出:

Templated length()
5
Templated length()
5
Templated length()
3

我的问题是为什么要更改此行中的代码(删除&lt;E&gt;):

    return  v | std::views::transform(length);

给我一​​大堆错误,开头是:Error C2672 'operator __surrogate_func': no matching overloaded function found?

为什么编译器不推断类型是std::string如果我用非模板函数替换模板:

auto length(const std::string& s) -> int
{
    std::cout << "Specialized length()\n";
    return static_cast<int>(s.length());
}

代码编译并运行,很明显,没有模板,编译器会找到我正在使用的特定类型的匹配项。

【问题讨论】:

    标签: c++ templates c++20


    【解决方案1】:

    这与视图无关。您可以将问题简化为:

    template <typename T>
    int length(T const& x) { return x.length(); }
    
    template <typename F>
    void do_something(F&& f) {
       // in theory use f to call something
    }
    
    void stuff() {
        do_something(length); // error
    }
    

    C++ 并没有真正进行类型推断。当您拥有do_something(length) 时,我们需要选择哪个我们正在谈论的length就在那里。我们不能这样做,所以这是一个错误。 do_something 没有办法说“我想要用 std::string 调用的函数模板的实例化 - 完全取决于调用者给 do_something 正确的东西。

    在原始示例中也是如此。 length&lt;E&gt; 是一个具体的函数。 length 不是随便传进去的。

    典型的方法是通过将函数模板包装在 lambda 中来延迟实例化:

    void stuff() {
        do_something([](auto const& e) { return length(e); }); // ok
    }
    

    现在,这行得通 - 因为 lambda 是一个表达式,它的类型可以由 do_something 推断,而 length 不是。而且我们不用手动提供模板参数,容易出错。

    我们可以用宏来概括这一点:

    #define FWD(arg) static_cast<decltype(arg)&&>(arg)
    #define LIFT(name) [&](auto&&... args) -> decltype(name(FWD(args)...)) { return name(FWD(args)...); }
    
    void stuff() {
        do_something(LIFT(length));
    }
    

    这避免了一些额外的输入,并且可能使意图更清晰。

    【讨论】:

    • 谢谢。 lambda 路线非常适合我正在做的事情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-22
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多