如何通过参数的某种性质而不是其确切类型来指定参数类型?

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;
}

您可以更进一步,使用 concepts or SFINAE 将您的模板限制为仅接受您应用的操作的类型。

假设您可以访问 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 字符串的情况下。