【问题标题】:ignore template parameter in type matching在类型匹配中忽略模板参数
【发布时间】:2021-06-14 13:04:05
【问题描述】:

这是我目前的程序:

#include <type_traits>

template<class Feature, class... FeatureList>
struct has_feature {
    static constexpr bool value = (std::is_same_v<Feature, FeatureList> || ...);
};

template<class Feature, class... FeatureList>
inline constexpr bool has_feature_v = has_feature<Feature, FeatureList...>::value;

template<class Feature, class ...FeatureList>
static constexpr bool isConfiguredWith() {
    return has_feature_v<Feature, FeatureList...>;
}

struct CanWalk {
};

struct CanNotWalk {
};

template<class... FeatureList>
struct Robot {
    static auto configure() {
        return Robot<WalkFeature < FeatureList...>>
        ();
    }

private:
    template<typename ...Config>
    using WalkFeature =
    std::conditional_t<isConfiguredWith<CanWalk, Config...>(), CanWalk, CanNotWalk>;
};

int main() {
    Robot<CanWalk> robot_A = Robot<CanWalk>::configure();
    Robot<CanNotWalk> robot_B = Robot<>::configure();

    return 0;
}

基本上,Robot 是一个结构体,可以配置许多其他结构体(它们在此处用作标记),然后将Robot&lt;T...&gt;::configure() 修剪并组织传递给Robot 的模板参数。最后我们有:

    Robot<CanWalk> robot_A = Robot<CanWalk, CanWalk>::configure();
    Robot<CanNotWalk> robot_B = Robot<>::configure();

虽然重复的特征CanWalk作为模板参数传入,但在通过函数configure()构造Robot时,它们都被删除了。

在我将模板参数添加到功能 CanWalk 之前,这一直很好:

template <int Speed>
struct CanWalk {
};

现在一切都中断了,因为 CanWalk 不再是合法类型,它需要一个模板参数。

对于错误error: use of class template 'CanWalk' requires template arguments 发生自:

template<typename ...Config>
using WalkFeature =
std::conditional_t<isConfiguredWith<CanWalk>(), CanWalk, CanNotWalk>;

我该如何解决?

我如何将它们定义为:

Robot<CanWalk<5>> robot_A = Robot<CanWalk<5>>::configure();
Robot<CanNotWalk> robot_B = Robot<>::configure();

?

直播代码:https://godbolt.org/z/4K9TxohWq

【问题讨论】:

  • 您打算为功能添加更多参数吗?只有非类型参数,还是类型参数?
  • @463035818_is_not_a_number 目前只有 1 个,未来可能更多。这会有很大的不同吗?
  • 如果类型参数和非类型参数混在一起,估计会很头疼。
  • 김선달 所说的。如果您打算混合使用类型和非类型,可能值得考虑将它们全部设为类型参数并使用std::integral_constant 或类似的非类型
  • 在某些时候,不管怎样,实际速度必须来自某个地方:程序必须提到,例如,CanWalk&lt;42&gt;。这将在您的设计中发生在哪里以及如何发生?如果CanWalk 只与假设的AnySpeed 参数一起使用,则相当于根本不将其用作模板。

标签: c++ templates template-meta-programming


【解决方案1】:

我在这里看到了三个挑战:

  1. 您需要从参数包中提取特定特征 (CanWalk),无论其参数如何
  2. 您需要忽略相同特征的额外副本
  3. 如果该特征不存在,您需要设置默认特征 (CanNotWalk)

我不知道比递归更好的方法:

// definition
template<template<auto...> class FeatureType, class DefaultFeature, class... FeatureList>
struct find_feature;

// next trait matches, stop recursing and return it
template<template<auto...> class FeatureType, class DefaultFeature, auto... Param, class... RemainingFeatures>
struct find_feature<FeatureType, DefaultFeature, FeatureType<Param...>, RemainingFeatures...> {
    using type = FeatureType<Param...>;
};

// next trait does not match, skip by inheriting from rest of list
template<template<auto...> class FeatureType, class DefaultFeature, class FirstFeature, class... RemainingFeatures>
struct find_feature<FeatureType, DefaultFeature, FirstFeature, RemainingFeatures...> 
    : find_feature<FeatureType, DefaultFeature, RemainingFeatures...> { };

// no more traits, return default trait
template<template<auto...> class FeatureType, class DefaultFeature>
struct find_feature<FeatureType, DefaultFeature> {
    using type = DefaultFeature;
};

// alias
template<template<auto...> class FeatureType, class... FeatureList>
using find_feature_t = typename find_feature<FeatureType, FeatureList...>::type;

这应该适用于作为类模板和任意数量的非类型参数的任何功能。

用法:

template <int speed>
struct CanWalk {};
struct CanNotWalk {};

template<class... FeatureList>
struct Robot {
    static auto configure() {
        return Robot<WalkFeature < FeatureList...>>
        ();
    }

private:
    template<typename ...Config>
    using WalkFeature = find_feature_t<CanWalk, CanNotWalk, Config...>;
};

auto robot_A = Robot<CanWalk<42>, CanWalk<25>>::configure();
static_assert(std::is_same_v<decltype(robot_A), Robot<CanWalk<42>>>, "");

auto robot_B = Robot<>::configure();
static_assert(std::is_same_v<decltype(robot_B), Robot<CanNotWalk>>, "");

【讨论】:

    【解决方案2】:

    删除一些通用性,您可能会执行以下操作:

    template <typename T> struct Tag { using type = T; };
    
    template <std::size_t> struct CanWalk {};
    struct CanNotWalk {};
    
    template <std::size_t N> Tag<CanWalk<N>> has_walk(Tag<CanWalk<N>>); // No impl
    template <typename T> Tag<CanNotWalk> has_walk(Tag<T>); // No impl
    
    template<class... FeatureList>
    struct Robot {
        static auto configure() {
            struct all_features : Tag<FeatureList>..., Tag<struct Empty> {};
            return Robot<typename decltype(has_walk(all_features{}))::type>{};
        }
    };
    
    int main() {
        [[maybe_unused]] Robot<CanWalk<42>> robot_A = Robot<CanWalk<42>>::configure();
        [[maybe_unused]] Robot<CanNotWalk> robot_B = Robot<>::configure();
    }
    

    Demo

    这种简单的方法不能处理重复,但可以做一些额外的工作(想法是让struct all_features : Tag&lt;Ts, Is&gt;...std::index_sequence

    【讨论】:

    • 如何使用float 而不是std::size_t 作为CanWalk 的模板参数?
    • @Rahn 使用 C++20 (example)
    • @TedLyngmo 该死,clang 还没有完全支持 C++ 20。但是感谢代码。
    • std::ratio 可能是 C++20 之前的替代方案(或 Functor 返回给定的浮点数)。
    猜你喜欢
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多