使用 std::views 的模板类型推断
Template type inference using std::views
我对函数式编程的了解有些迟了,并且开始思考 ranges/views。我正在使用 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
我的问题是为什么要更改此行中的代码(删除 <E>
):
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());
}
代码编译并运行,很明显没有模板,编译器找到了我正在使用的特定类型的匹配项。
这与观点无关。您可以将问题简化为:
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<E>
是具体函数。 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));
}
这避免了一些额外的输入,并且可能使意图更加清晰。
我对函数式编程的了解有些迟了,并且开始思考 ranges/views。我正在使用 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
我的问题是为什么要更改此行中的代码(删除 <E>
):
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());
}
代码编译并运行,很明显没有模板,编译器找到了我正在使用的特定类型的匹配项。
这与观点无关。您可以将问题简化为:
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<E>
是具体函数。 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));
}
这避免了一些额外的输入,并且可能使意图更加清晰。