合并类模板默认参数和可变参数

时间:2018-02-23 16:37:28

标签: c++ templates variadic-templates

我正在设计一个Host类,它使用固定数量的Policies类,大约3或4个。

因为设计中的每个Policy都会有一个简单的实现,所以将模板参数默认为普通类型会很好,以便轻松使用Host类由其客户。

class IdiotBenchPolicy {};
class IdiotLoggerPolicy {};
// ...

template <typename BenchmarkPolicy = IdiotBenchPolicy, 
          typename LoggerPolicy = IdiotLoggerPolicy>
class Host
{
     BenchmarkPolicy m_bench;
     IdiotLoggerPolicy m_logger;
};

这允许实例化Host而无需指定一长串模板参数。

这很好,直到Host类也必须采用可变数量的算术类型。在分割问题并忘记Policies时,我可以使用可变参数模板参数:

template <class... Args>
class Host
{
    template <class ...Var_Args>
    using are_arithmetic = typename std::conjunction<std::is_arithmetic<Var_Args>...>::type;

    static_assert(are_arithmetic<Args...>::value, "Args are not all arithmetic type");
    std::tuple<Args...> m_args;
};

请注意,即使Host在内部使用元组,最好不要在类客户端上强制执行它,除非必要。

我现在的问题是将这两种行为结合起来。如果“默认”足够,如何在不必指定Host的情况下实例化Policies类。我不知道如何用C ++模板语法来表达这一点,但我的直觉告诉我可以通过SFINAE和std :: enable_if进行操作,但是我很难知道如何。

我希望能写的是:

Host<int, int, float> h; //Using both default policies
Host<SmarterBenchPolicy, float, double> h2; //Using SmarterBenchPolicy, IdiotLoggerPolicy and two arithmetic types
Host<SmarterBenchPolicy>; //Using SmarterBenchPolicy, IdiotLoggerPolicy and no arithmetic type

如何使用类似于上面指定的实例化构建的Host类如何实现?

2 个答案:

答案 0 :(得分:1)

我认为如果将那些带有默认值的参数放在参数列表中,会更好。可变参数可以打包到std::tuple

template <class ArgsPack,
          typename BenchmarkPolicy = IdiotBenchPolicy,
          typename LoggerPolicy = IdiotLoggerPolicy,
          std::enable_if_t<detail::is_tuple<ArgsPack>::value, int> = 0
          >
struct Host
{
    BenchmarkPolicy     bench;
    LoggerPolicy        logger;
    ArgsPack            args;

    void show_all() {
        detail::tuple_foreach([](const auto &e) {std::cout << e << "\n";}, args);
    }
};

Host<std::tuple<int, int, float>, SmartBenchPolicy> h;

答案 1 :(得分:0)

这是一种方法。除了一个之外,它必须扩展到两个Policy参数。

#include <iostream>
#include <type_traits>
#include <tuple>

struct BenchPolicyBase {

};

struct BenchPolicy1 : BenchPolicyBase {

};

template <class enabler, class... Args>
class HostClass
{
    //Use default policy
    public:
    HostClass() { std::cout << "default" << std::endl; }
    template <class ...Var_Args>
    using are_arithmetic = typename std::conjunction<std::is_arithmetic<Var_Args>...>::type;

    static_assert(are_arithmetic<Args...>::value, "Args are not all arithmetic type");
    std::tuple<Args...> m_args;

    BenchPolicyBase m_bench;
};

template <typename T, typename... Rest>
using Host = HostClass<void, T, Rest...>;

template <class BenchPolicy, class... Args>
class HostClass<std::enable_if_t<std::is_base_of<BenchPolicyBase, BenchPolicy>::value>, BenchPolicy, Args...>
{
    //Use BenchPolicy
    public:
    HostClass() { std::cout << "Bench" << std::endl; }
    template <class ...Var_Args>
    using are_arithmetic = typename std::conjunction<std::is_arithmetic<Var_Args>...>::type;

    static_assert(are_arithmetic<Args...>::value, "Args are not all arithmetic type");
    std::tuple<Args...> m_args;

    BenchPolicy m_bench;
};

int main() {
    Host<int, long> a;
    Host<BenchPolicy1, int, long> b;
    return 0;
}