用于多个继承断言的可变参数模板 - “......用 3 个模板参数重新声明......”

Variadic templates for multiple heritage assertions - "...redeclared with 3 template parameters..."

我正在尝试为我的 AVR 编程实现我自己的 std::is_base_of(avr-gcc 尚不支持 <type_traits>。我从 可能的实现中获得了灵感the cppreference page 上,它适用于 single 类型检查。但是,我想要实现的是 multiple[=66 的静态执行有效性检查=] 类型继承一个基数 class。

为简单起见,我使用std::is_base_of进行下面的实际检查,但是我的实际解决方案接近于在上面链接的 cppreference 页面中。

我将使用它进行标签分发,更具体地说,允许以任何顺序排列选项标签。


选项标签

struct tOption {};
struct tThis : public tOption {};
struct tThat : public tOption {};
struct tElse {}; // Wrongly defined option tag!

单一继承验证器结构

template<typename TBase, typename TCandidate>
struct isBaseOf {
    isBaseOf() = delete;
    static const bool value = std::is_base_of<TBase, TCandidate>::value;
};

static_assert(isBaseOf<tOption, tThat>::value, "Invalid option tag!"); // OK!
static_assert(isBaseOf<tOption, tElse>::value, "Invalid option tag!"); // ERROR! Invalid option tag!

尝试多次检查(对上述 isBaseOf 声明的补充)

template<typename TBase, typename TCandidate, typename... TRest>
struct isBaseOf {
    isBaseOf() = delete;
    static const bool value = isBaseOf<TBase, TRest...>::value && 
                              std::is_base_of<TBase, TCandidate>::value;
};

这不起作用。据我所知,我无法使用不同数量的类型重新声明模板。但是,我在最后一个模板构造中至少需要两种类型。我尝试将 TBase 作为唯一参数并将值设置为 true,但同样的问题仍然存在:error: redeclared with 3 template parameters


用法

如前所述,这仅限于单次检查。由于我的 class(此处未显示)对任意数量的选项标签使用可变参数模板(并且 avr-gcc 不支持 constexpr 函数中带有 for 循环的完整 c++14),我想能够使用参数解包并仍然检查所有选项标签是否继承了我的基本标签 (tOption)。

template<typename... TOptions>
class tMyClass {
    static_assert(isBaseOf<tOption, TOptions...>::value, "Invalid option tag(s)!"); // <--- THIS
    // ...
};

使用函数 - 丑陋且不需要

我使用一个函数而不是另一个结构让它工作,但我认为这令人困惑。我宁愿有一种方法来解决整个递归(静态)堆栈中的问题。此外,这迫使我构建每个标签,这在我看来不是很整洁。

template<typename TBase, typename TCandidate>
constexpr bool isBaseOf2(const TBase&, const TCandidate&) {
    return std::is_base_of<TBase, TCandidate>::value;
}

template<typename TBase, typename TCandidate, typename... TRest>
constexpr bool isBaseOf2(const TBase& base, const TCandidate&, const TRest&... rest) {
    return isBaseOf2(base, rest...) && std::is_base_of<TBase, TCandidate>::value;
}

static_assert(isBaseOf2(tOption{}, tThis{}, tThat{}), "Invalid option tag(s)!"); // OK!
static_assert(isBaseOf2(tOption{}, tThis{}, tElse{}), "Invalid option tag(s)!"); // ERROR! Invalid option tag(s)!

是否有任何方法可以使用其他数量的参数重新定义结构模板,例如上面的尝试多次检查

在 c++17 中,您可以在 && 运算符上使用 fold-expression 来实现这一点

template<typename Base, typename ...Candidates>
struct is_base_of_multiple {
    static constexpr bool value = (std::is_base_of_v<Base, Candidates> && ...); // change std::is_base_of_v to your own implementation
};

如果您不能使用 c++17,但可以使用 c++11,这是另一种仅使用可变参数模板的方法

template <typename Base, typename First, typename ...Rest>
struct is_base_of_multiple {
    static constexpr bool value = std::is_base_of<Base, First>::value && is_base_of_multiple<Base, Rest...>::value;
};

template <typename Base, typename Candidate>
struct is_base_of_multiple<Base, Candidate> {
    static constexpr bool value = std::is_base_of<Base, Candidate>::value;
};

问题

template<typename TBase, typename TCandidate, typename... TRest>
struct isBaseOf {
    isBaseOf() = delete;
    static const bool value = isBaseOf<TBase, TRest...>::value && 
                              std::is_base_of<TBase, TCandidate>::value;
};

就这样结束了吗,你完成了:

static const bool value = isBaseOf<TBase, /*Empty Pack*/>::value && 
                          std::is_base_of<TBase, TCandidate>::value;

isBaseOf<TBase, TRest...>空包无效

您必须添加专门化来处理这种情况:

template<typename TBase, typename TCandidate>
struct isBaseOf<TBase, TCandidate> {
    isBaseOf() = delete;
    static const bool value = std::is_base_of<TBase, TCandidate>::value;
};

没有递归的替代方案:

template <bool... Bs> struct Bools{};
template <bool... Bs> using All = std::is_same<Bools<true, Bs...>, Bools<Bs..., true>>;

template<typename TBase, typename... TCandidates>
using isBaseOf = All<std::is_base_of<TBase, TCandidates>::value...>;