【问题标题】:How do I combine two template functions that have the same code?如何组合具有相同代码的两个模板函数?
【发布时间】:2021-10-27 10:51:54
【问题描述】:

为了调试代码,我正在尝试创建一个用于轻松输出容器的模板。我希望能够做到

int a = 3;
pair<int,int> b = {2,3};
vector<int> c = {1,2,3,4};
map<int,int> m;
m[2] = 3;
m[3] = 4;
dbg(a,b,c,m);

OUTPUT:
[a,b,c,m] = [ 3 , (2,3) , {1,2,3,4} , {(2,3),(3,4)} ]

到目前为止,我有这个:

#define dbg(x...) cout << "[" << #x << "] = [ "; _dbg(x); cout << "]"

template <typename T> void _dbg(T t)
{
    cout << t << " ";
}
template<typename T, typename... Args>
void _dbg(T t, Args... args) // recursive variadic function
{
    cout << t << " , ";
    _dbg(args...);
}

template <typename T, typename V>
ostream& operator<<(ostream& os, const pair<T, V> p)
{
    cout << "(" << p.first << "," << p.second << ")";
}

template <typename T>
ostream& operator<<(ostream& os, const vector<T>& dt)
{
    cout << "{";
    auto preEnd = dt.end();
    preEnd--;
    for (auto bgn = dt.begin(); bgn != preEnd; bgn++)
        cout << *bgn << ",";
    cout << *preEnd << "}";
    return os;
}

template <typename T>
ostream& operator<<(ostream& os, const set<T>& dt)
{
    cout << "{";
    auto preEnd = dt.end();
    preEnd--;
    for (auto bgn = dt.begin(); bgn != preEnd; bgn++)
        cout << *bgn << ",";
    cout << *preEnd << "}";
    return os;
}

template <typename T, typename V>
ostream& operator<<(ostream& os, const map<T, V>& dt)
{
    cout << "{";
    auto preEnd = dt.end();
    preEnd--;
    for (auto bgn = dt.begin(); bgn != preEnd; bgn++)
        cout << *bgn << ",";
    cout << *preEnd << "}";
    return os;
}

而且效果很好!只是我不想为每个容器类型定义一个函数,因为它们都具有相同的主体(谈论最后 3 个函数)。 我尝试了类似的东西

template<typename C, typename T>
ostream& operator<<(ostream& os, const C<T>& dt)

但我得到了

error: C is not a template

我试过了

template<typename C>
ostream& operator<< (ostream& os, const C& dt)

得到了

error: no match for 'operator<<' (operand types are 
'std::ostream {aka std::basic_ostream<char>}' and 'std::set<int>')|

那么我如何只拥有一个处理任何容器的通用函数(例如,一个是 template&lt;typename T&gt; vector&lt;T&gt;,另一个是 set&lt;T&gt;??)

【问题讨论】:

  • 使用迭代器(在辅助函数中)代替容器,并将容器传递给接受模板模板参数的单个函数,然后调用该辅助函数!
  • 您能否提供一些示例代码来说明接受模板模板参数的函数?我以前从未听说过。
  • 这能回答你的问题吗? Template class with template container

标签: c++ algorithm templates operator-overloading function-templates


【解决方案1】:

我如何只拥有一个处理任何容器的通用函数?

正如 @Const 在评论部分已经提到的,您可以提供一个模板化的operator&lt;&lt; 重载,它具有template template parameter,以便它可以接受您选择的容器,其范围将是传递给辅助函数(即以下示例中的print_helper)以打印元素。这要求容器的元素定义operator&lt;&lt;;就像您为 std::pair 类型提供了重载一样。

template <typename Iterator>
std::ostream& print_helper(std::ostream& os
    , Iterator start, const Iterator end) noexcept
{
    for (; start != end; ++start) os << *start;
    return os << '\n';
}

// template template parameter
template <template<typename Type, typename... Rest> class Conatiner, typename Type, typename... Rest> 
std::ostream& operator<<(std::ostream& os
    , const Conatiner<Type, Rest...>& container) noexcept
{
    // pass the ranges to helper function
    return print_helper(os, std::begin(container), std::end(container)); 
}

这适用于 ,这里是 (the complete example code)

【讨论】:

    【解决方案2】:

    如果您使用 C++ 20,则通过将模板限制在概念上会更容易。

    注意std::stringchar[] 都是满足 std::ranges::range 的类型,但我们不希望它们使用我们的模板

    template <typename T>
    concept non_string_range = std::ranges::range<T> && !std::is_same_v<std::ranges::range_value_t<T>, char>;
    
    template <non_string_range T>
    std::ostream& operator<<(std::ostream& os, const T & range) {
        std::cout << "{";
        std::string prefix;
        for (auto & elem : range) {
            std::cout << prefix << elem;
            prefix = ",";
        }
        std::cout << "}";
        return os;
    }
    

    在 C++20 之前,您可以使用 trait 和 std::enable_if 做类似的事情

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 2020-01-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多