c++ - 在不显式指定其余参数的情况下设置默认模板非类型参数

标签 c++ templates c++11 stl containers

先上代码供引用(不用担心<<重载,原来一切都在namespace里):

#include <bits/stdc++.h>

// structures for checking if given variable is container
template <typename Container>
struct is_container : std::false_type { };

template<typename... Ts>
struct is_container<std::vector<Ts...>> : std::true_type { };

template<typename... Ts>
std::ostream& operator << (std::ostream& os, std::vector<Ts...>) {return os;}

template<typename... Ts>
struct is_container<std::deque<Ts...>> : std::true_type { };

template<typename... Ts>
std::ostream& operator << (std::ostream& os, std::deque<Ts...>) {return os;}

template<typename... Ts>
struct is_container<std::list<Ts...>> : std::true_type { };

template<typename... Ts>
std::ostream& operator << (std::ostream& os, std::list<Ts...>) {return os;}
//... the rest of containers in like manner, not necessary for this example

//function that prints given container (begin() and end() are required)
//this function is instantiated also for non-printable containers, but then is never called
//that's why I had to overload << operator, so it may instantiate for them
template<template<typename ...>
         class C ,
         typename T>
void print(const C<T>& cont,
           const char& separator = ',',
           const std::deque<std::pair<char,char>>& closures = {{'(', ')'}});

//function that prints to console given nested container. Container has to be at least one-level nested
//so it will accept f.e. vector of vectors of ints, but will reject vector of int
template<int Depth,
         template<typename ...>
         class C ,
         typename T,
         typename = typename std::enable_if<is_container<T>::value>::type>
void print_nested(const C<T>& cont,
                  const char& separator = ',',
                  const std::deque<std::pair<char,char>>& closures = {{'{','}'},{'[',']'},{'(',')'}},
                  unsigned depth = 0 );

//placebo template function to handle calls, where containers aren't nested (kind of fake instantiation)
template<int Depth, typename T>
void print_nested(const T&,
                  const char& ,
                  const std::deque<std::pair<char,char>>&,
                  unsigned);

//definition of above
template<int Depth, typename T>
void print_nested(const T&,
                  const char& ,
                  const std::deque<std::pair<char,char>>&,
                  unsigned)
{}

//definition of function printing non-nested container
template<template<typename ...> class C , typename T>
void print(const C<T>& cont,
           const char& separator,
           const std::deque<std::pair<char,char>>& closures)
{
    std::cout << closures[0].first;
    for(auto it=cont.begin(); it!=cont.end(); )
    {
        std::cout << *it;
        if(++it != cont.end())
            std::cout << separator << " ";
    }
    std::cout << closures[0].second;
}

//definition of main function, which handles printing nested containers
template<int Depth,
         template<typename ...> class C ,
         typename T,
         typename = typename std::enable_if<is_container<T>::value>::type>
void print_nested(const C<T>& cont,
                  const char& separator,
                  const std::deque<std::pair<char,char>>& closures,
                  unsigned depth)
{
    if(depth < Depth)
        ++depth;
    std::cout << closures[0].first;
    for(auto it=cont.begin(); it!=cont.end(); )
    {
        if(it!=cont.begin())
            std::cout << std::string(depth, ' ');
        if(is_container<typename T::value_type>::value)
            print_nested<Depth>(*it,
                                separator,
                                (closures.size() > 1)?
                                std::deque<std::pair<char,char>>(closures.begin()+1, closures.end()) : closures,
                                depth
                                );
        else
            print(*it,
                  separator,
                  (closures.size() > 1)?
                  std::deque<std::pair<char,char>>(closures.begin()+1, closures.end()) : closures
                  );
        if(++it != cont.end())
            std::cout << separator << std::endl;
    }
    std::cout << closures[0].second;
}

void gimme_some_space(std::string anger = "")
{
    std::for_each(anger.begin(), anger.end(), [](char c) {if(c=='!') std::cout << std::endl;});
}

int main()
{
    //cases and calls:
    std::vector<std::deque<std::list<int>>> nested {{{1,2,3},{3,4,5},{7,8,9}},{{9,8,7},{6,5,4},{3,2,1}}};
    std::vector<int> not_nested {1,2,3,4,5};

    print_nested<2>(nested); // target functionality
    gimme_some_space("!!");
    print_nested<0>(nested); // i'd like to omit <0> (set it as default non-type template parameter)
    //print_nested<0>(not_nested); //excluded from overload set, non_nested is not nested
    gimme_some_space("!!!");
    print(not_nested); //call for printing non-nested container
    gimme_some_space("!!!!");
}

它的工作。这是输出:

{[(1, 2, 3),
  (3, 4, 5),
  (7, 8, 9)],
 [(9, 8, 7),
  (6, 5, 4),
  (3, 2, 1)]}

{[(1, 2, 3),
(3, 4, 5),
(7, 8, 9)],
[(9, 8, 7),
(6, 5, 4),
(3, 2, 1)]}


(1, 2, 3, 4, 5)

我的问题是:参数 Depth 决定缩进。它始终应该等于嵌套级别,对于非嵌套容器,它应该为 0。但我什至不希望它以某种方式检索有关给定容器嵌套多少的信息 - 相反 - 我想设置深度默认值为零。所以在模板参数中应该是:

int Depth = 0

但这使事情复杂化了。如果它有默认值,它不能放在参数列表的开头,因为容器 C 和容器 T 的元素(当然是类型)不是默认值。所以无论如何让我们这样做:

template<template<typename ...> class C ,
         typename T,
         int Depth = 0,
         typename = typename std::enable_if<is_container<T>::value>::type>

近乎完美。是吗? 这现在适用于深度设置默认为 0:

print_nested(nested);

但是如果我现在想自己指定缩进怎么办。恐怖:

print_nested<std::vector<std::deque<std::list<int>>>, std::deque<std::list<int>>, 2>(nested);

你知道克服这个问题的任何令人兴奋的方法吗?

最佳答案

你可以使用额外的参数。

std::integral_constant<std::size_t, Depth > maxDepth怎么样? , 所以

template <std::size_t N> using Depth_t = std::integral_constant<std::size_t, N>;

template<template<typename ...> class C ,
         typename T,
         std::size_t Depth = 0,
         typename = typename std::enable_if<is_container<T>::value>::type>
void print_nested(const C<T>& cont,
                  Depth_t<Depth> = {},
                  const char& separator = ',',
                  const std::deque<std::pair<char,char>>& closures = {{'{','}'},{'[',']'},
                  unsigned depth = 0
                  );

这样称呼

print_nested(nested, Depth_t<2>{});

关于c++ - 在不显式指定其余参数的情况下设置默认模板非类型参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35493975/

相关文章:

c++ - 并发:C++11 内存模型中的原子性和 volatile

c++ - 对给定结构成员应用操作的通用方法

c++ - boost::iterator_facade 和 std::find(...)

c++ - C++ 中的 MSD 基数排序(字典顺序)

c++ - 为自定义 C++ 类实现 Visual Studio 的自定义向导

c++ - 如何创建C++宏来定义使用调用另一个函数的参数列表的函数?

c++ - 在编译时通过模板自动判断类型是否为抽象基类

C++ 函数斜杠运算符 lambda 表达式

c++ - 使用模板访问成员变量

c++ - 错误 "invalid operands to binary expression"