c++ - 只接受指向算术类型的迭代器的模板
问题描述
我正在尝试自学 SFINAE 模式,对于我正在写的东西,我想编写一个函数,该函数接受start
,end
迭代器到算术类型的值(例如,用于求和)。这就是我想出的:
我的main.cpp
:
#include <iostream>
#include <vector>
#include "summer.hpp"
int main()
{
std::vector<double> vec {0.1, 0.2, 0.3}; // these are OK
auto sum = summer(vec.begin(), vec.end());
std::cout << sum << std::endl;
std::vector<std::string> vec2 {"a", "b"}; // these should be rejected
auto sum2 = summer(vec2.begin(), vec2.end());
std::cout << sum2 << std::endl;
return 0;
}
然后summer.hpp
:
#include <type_traits>
template <
typename Iter,
typename = typename std::enable_if_t<std::is_arithmetic<Iter>::value_type, Iter>,
typename T = typename Iter::value_type
>
T summer(Iter start, Iter end)
{
T sum{};
for (auto it = start; it != end; it++)
{
sum += *it;
}
return sum;
}
我从这个答案中获取了上面的 SFINAE 位,并对其进行了调整以使用与迭代器指向的类型相对应的类型特征 ( value_type
)。但我正在努力编译它,我收到一连串关于value_type
被解析为非类型但产生类型和提示的抱怨,我应该在它前面加上添加typename
(这是错误的):
$ g++ --std=c++17 main.cpp && ./a.out
main.cpp: In function ‘int main()’:
main.cpp:10:45: error: no matching function for call to ‘summer(std::vector<double>::iterator, std::vector<double>::iterator)’
10 | auto sum = summer(vec.begin(), vec.end());
| ^
In file included from main.cpp:4:
summer.hpp:8:3: note: candidate: ‘template<class Iter, class, class T> T summer(Iter, Iter)’
8 | T summer(Iter start, Iter end)
| ^~~~~~
summer.hpp:8:3: note: template argument deduction/substitution failed:
summer.hpp:5:5: error: dependent-name ‘std::is_arithmetic<_Tp>::value_type’ is parsed as a non-type, but instantiation yields a type
5 | typename = typename std::enable_if_t<std::is_arithmetic<Iter>::value_type, Iter>,
| ^~~~~~~~
summer.hpp:5:5: note: say ‘typename std::is_arithmetic<_Tp>::value_type’ if a type is meant
main.cpp:13:48: error: no matching function for call to ‘summer(std::vector<std::__cxx11::basic_string<char> >::iterator, std::vector<std::__cxx11::basic_string<char> >::iterator)’
13 | auto sum2 = summer(vec2.begin(), vec2.end());
| ^
In file included from main.cpp:4:
summer.hpp:8:3: note: candidate: ‘template<class Iter, class, class T> T summer(Iter, Iter)’
8 | T summer(Iter start, Iter end)
| ^~~~~~
summer.hpp:8:3: note: template argument deduction/substitution failed:
summer.hpp:5:5: error: dependent-name ‘std::is_arithmetic<_Tp>::value_type’ is parsed as a non-type, but instantiation yields a type
5 | typename = typename std::enable_if_t<std::is_arithmetic<Iter>::value_type, Iter>,
| ^~~~~~~~
summer.hpp:5:5: note: say ‘typename std::is_arithmetic<_Tp>::value_type’ if a type is meant
我相信我已经放在typename
正确的地方,如果我要摆脱算术限制编译就好了,但我不希望它接受例如string
向量。
我究竟做错了什么?
解决方案
好吧,我从哪里开始...
typename std::enable_if_t<...>
错了,删typename
。::
仅当模板参数右侧存在时才需要它,例如在typename std::enable_if<...Iter...>::type
.::value_type
放错地方了,应该就在后面Iter
。...::value_type
需要typename
。std::is_arithmetic<...>
必须是std::is_arithmetic_v<...>
或std::is_arithmetic<...>::value
在这种情况下,第二个模板参数
std::enable_if_t
无关紧要,可以删除。
所以我们最终得到了这段代码,它至少可以工作:
template <
typename Iter,
typename = std::enable_if_t<std::is_arithmetic_v<typename Iter::value_type>>,
typename T = typename Iter::value_type
>
但是等等,还有更多:
typename T = typename Iter::value_type
是对模板参数的滥用,可以通过指定自定义模板参数来破坏。typename = std::enable_if_t<...>
是一个弱 SFINAE,因为用户可以通过提供任何模板参数来规避它。更喜欢这种形式:std::enable_if_t<..., std::nullptr_t> = nullptr
,它没有这个问题。您应该使用
std::iterator_traits
而不是直接从迭代器中读取::value_type
,因为某些迭代器(例如指针)没有它(感谢@NathanPierson)。auto it = start;
执行不必要的副本。可以直接操作start
。
所以最终版本看起来像这样:
template <
typename Iter,
std::enable_if_t<std::is_arithmetic_v<typename std::iterator_traits<Iter>::value_type>, std::nullptr_t> = nullptr
>
typename std::iterator_traits<Iter>::value_type summer(Iter start, Iter end)
{
typename std::iterator_traits<Iter>::value_type sum{};
for (; start != end; start++)
sum += *start;
return sum;
}
推荐阅读
- p5.js - 创建嘈杂字体动画
- python - 如何从 tkinter 输入框中获取价值并在 Pandas 中使用它
- python - 提前停止使用 Pycaret?使用 Catboost 和 XGBoost 过拟合
- ruby-on-rails - 未在 Sidekiq 中注册的定期作业
- javascript - 如何使用 pdfkit 和 violab 创建具有不同标题的多个表?
- testing - 如何从 Gitlab 注入参数到 testcafe 命令行
- c# - 在 C# 中从数据库中获取 ID - 获取悬停面板的 ID
- javascript - Google Maps API 绘图库 - 有没有办法在拖动一点时更新形状坐标?
- heroku - 在heroku中部署后使环境变量工作
- xamarin.forms - Xamarin Forms:加载页面时未触发 onAppearing()