在类型匹配中忽略模板参数
ignore template parameter in type matching
这是我当前的程序:
#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
是一个struct,可以配置很多其他的struct(这里用它们作为token),然后Robot<T...>::configure()
trim和组织传入的模板参数至 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();
?
我在这里看到三个挑战:
- 您需要从参数包中提取特定特征 (
CanWalk
),而不考虑其参数
- 您需要忽略相同特征的额外副本
- 如果该特征不存在,您需要设置默认特征 (
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;
这应该适用于 class 模板和任意数量的非类型参数的任何功能。
用法:
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>>, "");
删除一些通用性,您可以这样做:
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();
}
这种简单的方法不能处理重复项,但可以做一些额外的工作(想法是让 struct all_features : Tag<Ts, Is>...
和 std::index_sequence
)
这是我当前的程序:
#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
是一个struct,可以配置很多其他的struct(这里用它们作为token),然后Robot<T...>::configure()
trim和组织传入的模板参数至 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();
?
我在这里看到三个挑战:
- 您需要从参数包中提取特定特征 (
CanWalk
),而不考虑其参数 - 您需要忽略相同特征的额外副本
- 如果该特征不存在,您需要设置默认特征 (
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;
这应该适用于 class 模板和任意数量的非类型参数的任何功能。
用法:
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>>, "");
删除一些通用性,您可以这样做:
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();
}
这种简单的方法不能处理重复项,但可以做一些额外的工作(想法是让 struct all_features : Tag<Ts, Is>...
和 std::index_sequence
)