如何通过参数的某种性质而不是其确切类型来指定参数类型?
How do I specify a parameter type by some quality of the parameter rather than its exact type?
所以我在C++中有一个函数如下:
void print_vec(std::vector<int> v) {
for (int i: v) {
std::cout << i << ' ';
}
std::cout << std::endl;
}
这一切都有效,但仅适用于 int 向量。我想要类似
的东西
void print_iter(Iterable<CanDoCout> iterable) {
for (auto i: iterable) {
std::cout << i << ' ';
}
std::cout << std::endl;
}
如何实施 print_iter
?或者我怎样才能找到如何实现这样的通用函数?
您要找的是templates:
template <class T>
void print_iter(const T& iterable) {
for (const auto& i: iterable) {
std::cout << i << ' ';
}
std::cout << std::endl;
}
假设您可以访问 C++20,这就是您使用概念限制模板接受的类型的方式:
template <typename T>
concept printable = requires(T t)
{
std::cout << t;
};
template <std::ranges::range T>
requires printable<std::ranges::range_value_t<T>>
void print_iter(const T &iterable)
{
for (const auto &it : iterable)
std::cout << it << ' ';
std::cout << '\n';
}
concept printable = ...
将 printable
定义为一个概念((通常)一种类型的一组要求)。当表达式 std::cout << t
格式正确时,这个概念得到满足,假设 t
是类型 T
.
的变量
在print_iter()
的定义中:
template <std::ranges::range T>
与 template <typename T>
相同,除了它用概念 std::ranges::range
(这就是你所说的“可迭代”)来约束类型。
requires printable<std::ranges::range_value_t<T>>
给T
增加了一个额外的约束:它要求类型std::ranges::range_value_t<T>
(T
的元素类型)满足我们的概念 printable
.
如果您不想使用 C++20 概念(或 Rust 特性),并且您仍然希望您的函数仅适用于可迭代类型(因为没有人喜欢 SFINAE 错误),一种技术是要求它有一个 const_iterator:
template <typename T>
void print_range(const T& iterable, typename T::const_iterator = T{}.cbegin()) {
for (const auto& elem : iterable) {
std::cout << elem << ' ';
}
std::cout << std::endl;
}
在这种情况下,const_iterator 参数必须是默认的虚拟变量。
我之前建议采用两个迭代器,但这要求调用者明确说明模板类型,这不是很酷。
对选择 const_iterator/cbegin 作为可迭代对象(通常不是这种技术)的定义特征的公平批评是,并非所有可迭代对象都有此接口。但这并不是真正的障碍,因为调用者可以将其包装在具有此接口的类型中。例如 string_view 在 C 字符串的情况下。
所以我在C++中有一个函数如下:
void print_vec(std::vector<int> v) {
for (int i: v) {
std::cout << i << ' ';
}
std::cout << std::endl;
}
这一切都有效,但仅适用于 int 向量。我想要类似
的东西void print_iter(Iterable<CanDoCout> iterable) {
for (auto i: iterable) {
std::cout << i << ' ';
}
std::cout << std::endl;
}
如何实施 print_iter
?或者我怎样才能找到如何实现这样的通用函数?
您要找的是templates:
template <class T>
void print_iter(const T& iterable) {
for (const auto& i: iterable) {
std::cout << i << ' ';
}
std::cout << std::endl;
}
假设您可以访问 C++20,这就是您使用概念限制模板接受的类型的方式:
template <typename T>
concept printable = requires(T t)
{
std::cout << t;
};
template <std::ranges::range T>
requires printable<std::ranges::range_value_t<T>>
void print_iter(const T &iterable)
{
for (const auto &it : iterable)
std::cout << it << ' ';
std::cout << '\n';
}
concept printable = ...
将 printable
定义为一个概念((通常)一种类型的一组要求)。当表达式 std::cout << t
格式正确时,这个概念得到满足,假设 t
是类型 T
.
在print_iter()
的定义中:
template <std::ranges::range T>
与template <typename T>
相同,除了它用概念std::ranges::range
(这就是你所说的“可迭代”)来约束类型。requires printable<std::ranges::range_value_t<T>>
给T
增加了一个额外的约束:它要求类型std::ranges::range_value_t<T>
(T
的元素类型)满足我们的概念printable
.
如果您不想使用 C++20 概念(或 Rust 特性),并且您仍然希望您的函数仅适用于可迭代类型(因为没有人喜欢 SFINAE 错误),一种技术是要求它有一个 const_iterator:
template <typename T>
void print_range(const T& iterable, typename T::const_iterator = T{}.cbegin()) {
for (const auto& elem : iterable) {
std::cout << elem << ' ';
}
std::cout << std::endl;
}
在这种情况下,const_iterator 参数必须是默认的虚拟变量。 我之前建议采用两个迭代器,但这要求调用者明确说明模板类型,这不是很酷。
对选择 const_iterator/cbegin 作为可迭代对象(通常不是这种技术)的定义特征的公平批评是,并非所有可迭代对象都有此接口。但这并不是真正的障碍,因为调用者可以将其包装在具有此接口的类型中。例如 string_view 在 C 字符串的情况下。