【问题标题】:Non-overloaded C++ Function That Can Print a vector and a vector of vectors?可以打印向量和向量向量的非重载C++函数?
【发布时间】:2020-06-03 23:55:12
【问题描述】:

考虑以下重载函数,它可以打印一维向量和多种类型的向量,如字符串、整数、双精度等。

template<typename T>
void p(const vector<vector<T>>& vec) {
    int count = 0;
    for (vector<T> innerVec: vec) {
        cout << count++ << ": ";
        for (T e :innerVec) {
            cout << e << ' ';
        }
        cout << '\n';
    }
    cout << '\n';
}


template<typename T>
void p(const vector<T>& vec) {
    for (T e: vec) {
        cout << e << ' ';
    }
    cout << '\n';
}

无论如何我可以将这两个函数合并为 1 吗?我尝试使用 SFINAE 和标签调度,但我能想出的所有解决方案都需要一个宏或多个函数,我不想要这个。

我知道这个问题可能看起来很奇怪,因为我的解决方案有效,但我更喜欢在我的代码中只包含一个函数。这是因为我想实现一个函数,它可以检测我是否传入了一个映射、向量、向量的向量、unordered_set、multimap 等,并且只打印该 STL 数据结构,并且每个专业化都有一个重载函数有点烦人因为它变得很快。

【问题讨论】:

  • 不是一个完整的解决方案,但您可以替换代码重复,在 2D 版本中,只需执行 cout &lt;&lt; innerVec; 另外,我真的建议通过 const &amp;
  • 无关:vector&lt;vector&lt;T&gt;&gt; vec 不是 2D vector。它是一个vector,包含更多vectors。因此,可能会对性能产生很大影响。数据并非全部在一个连续的内存块中,因此它对缓存不是特别友好。
  • 因为这两个函数的行为不同,所以在合并这两个函数时会遇到麻烦。您可以这样做:template&lt;typename T&gt; std::ostream &amp; operator&lt;&lt;(std::ostream &amp; out, const std::vector&lt;T&gt;&amp; vec) { for (T e: vec) { out &lt;&lt; e &lt;&lt; delimiter; } return out; },但分隔符会发生变化。您需要一些额外的巫术来检测何时处于叶子状态,因为没有更多容器,因此您可以交换分隔符。我承认吸巫术。
  • 如果您从所需的输出中删除0: 等,那么您只需要template&lt;typename Container, typename = std::void_t&lt;typename Container::value_type&gt;&gt; std::ostream &amp; operator&lt;&lt;(std::ostream &amp; out, const Container&amp; cont)template&lt;typename T1, typename T2&gt; std::ostream &amp; operator&lt;&lt;(std::ostream &amp; out, const std::pair&lt;T1, T2&gt;&amp; pair) 即可涵盖所有内容。

标签: c++ templates sfinae


【解决方案1】:

我今天回答了一个类似的问题。在那里查看:https://stackoverflow.com/a/60298735/8192043

在此处粘贴解决方案:

这应该适用于您的情况。请注意,我正在使用@Jarod42 https://stackoverflow.com/a/29634934/8192043 在这个惊人的解决方案中实现的特征。

template<template<typename ...> typename C, typename D, typename ... Others>
void foo(const C<D, Others...> &object)
{
    if constexpr(is_iterable<D>::value)
    {
       for(const auto& v : object)
       {
           for (const auto& w : v)
           {...}
       }
    }
    else
    {
       for (const auto& w : object)
       {...}
    }
}

Live Code

【讨论】:

  • 如果可能,我会尽量避免添加特征。我不想添加另一个函数/类/结构只是为了能够做这么简单的事情。但聪明的想法如此受欢迎。
【解决方案2】:

是的,但是你需要一个额外的参数来区分内壳和外壳

#include <vector>
#include <iostream>

struct counting_prefix {
    void call() { std::cout << count++ << ": "; }
    int count = 0;
};

struct no_prefix {
    void call() { }
};

template<typename T, typename Prefix = no_prefix>
void p(const T& e, Prefix prefix = {}) {
    prefix.call();
    std::cout << e << ' ';
}

template<typename T, typename Prefix = no_prefix>
void p(const std::vector<T>& vec, Prefix prefix = {}) {
    for (const T& e: vec) {
        prefix.call();
        p(e);
    }
    std::cout << '\n';
}

int main() {
    std::vector<std::vector<double>> stuff = { { 1., 2. }, { 3., 4. } };
    p(stuff, counting_prefix{});
}

See it live

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多