提问人:Irreducible Polynomial 提问时间:11/12/2023 最后编辑:Irreducible Polynomial 更新时间:11/13/2023 访问量:112
了解 SFINAE:部分专用类之外的成员函数声明,以及可变参数类中的模板
Understanding SFINAE : declarations of member functions outside of partially specialized classes, and templates within variadic classes
问:
我想做什么:
创建一个“InstructionSet”对象,该对象
- 通常(无需指定类型)由其他位置的容器(指针)持有
- 保存可变数量的指令对象(可能具有重复类型),其类型满足概念“is_instruction_type”
- 具有“extract”函数,可生成一个新的“InstructionSet”,其中包含单个特定类型的所有指令
我试图通过在可变参数类上使用继承来实现这一点,这样就可以使用递归来存储正确类型的每条指令;然后,指向类型“InstructionSet<>”的指针可以使用一个虚拟函数,以便能够访问继承的底部,并在对象上重复。
//Define recursively
template<typename...Args> requires (is_instruction_type<Args> && ...)
class InstructionSet;
//Base
template<>
class InstructionSet<>
{
public:
InstructionSet() {};
virtual ~InstructionSet() {};
template<typename U>
auto extract()
{
return extractHelper()->extract();
};
protected:
virtual InstructionSet<>* extractHelper();
};
//Recur
template<typename T, typename ...Rest> requires is_instruction_type<T>
class InstructionSet<T, Rest...> : public InstructionSet<Rest...>
{
public:
InstructionSet(T&& t, Rest&&...rest);
InstructionSet(T&& t, InstructionSet<Rest...>&& set);
virtual ~InstructionSet() {};
template<typename U> requires std::same_as<T, U>
auto extract();
template<typename U> requires !std::same_as<T, U>
auto extract();
virtual InstructionSet<T, Rest...>* extractHelper()
{
return this;
};
private:
T _instruction;
};
template<typename T, typename ...Rest> requires is_instruction_type<T>
inline InstructionSet<T, Rest...>::InstructionSet(T&& t, Rest&& ...rest) : InstructionSet<Rest...>(std::forward<Rest>(rest)...),
_instruction(std::forward<T>(t))
{
}
template<typename T, typename ...Rest> requires is_instruction_type<T>
inline InstructionSet<T, Rest...>::InstructionSet(T&& t, InstructionSet<Rest...>&& set)
{
_instruction = std::forward<T>(t);
std::construct_at((InstructionSet<Rest...>*)this, std::move(set));
}
template<typename T, typename ...Rest> requires is_instruction_type<T>
template<typename U> requires std::same_as<T, U>
auto InstructionSet<T, Rest...>::extract<U>()
{
return InstructionSet(_instruction, std::move(((InstructionSet<Rest...>*)this)->extract()));
}
template<typename T, typename ...Rest> requires is_instruction_type<T>
template<typename U> requires (!std::same_as<T, U>)
auto InstructionSet<T, Rest...>::extract<U>()
{
return ((InstructionSet<Rest...>*)this)->extract();
}
我有两个问题:
- 在多个模板下,应该如何在类之外定义“extract”?
- 在部分专用类定义中应如何处理类成员?(是否也应该有一个完整的类定义?
我尝试了包含/排除尖括号的不同组合,并尝试将函数定义移动到类内部,但都抛出了错误。
我认为我了解具有类型定义和静态变量的类的 SFINAE 基础知识,但是对于更复杂的类,我感到困惑,并且似乎找不到很多示例。
答:
0赞
Ted Lyngmo
11/12/2023
#1
我认为您可以通过不进行专业化并使用 a 来存储指令来简化它。std::tuple
首先,一个辅助函数,用于创建一个参数包中的所有索引,其中类型为:std::index_sequence
T
// get an index_sequence for where all T's are in Ts...
template <class T, class... Ts>
constexpr auto get_indices_for() {
constexpr auto inds_sel = []<std::size_t... Is>(std::index_sequence<Is...>) {
std::array<std::size_t, sizeof...(Ts)> indices{};
std::size_t sel = 0;
(..., (std::same_as<T, std::tuple_element_t<Is, std::tuple<Ts...>>> &&
(indices[sel++] = Is)));
return std::pair{indices, sel};
}(std::make_index_sequence<sizeof...(Ts)>{});
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::index_sequence<inds_sel.first[Is]...>{};
}(std::make_index_sequence<inds_sel.second>{});
}
然后,如果将指令存储在 中,提取可能如下所示:std::tuple
struct InstructionSetBase {
virtual ~InstructionSetBase() = default;
};
template <class... Ts>
requires(is_instruction_type<Ts> && ...)
class InstructionSet : public InstructionSetBase {
public:
template <class... Us>
InstructionSet(Us&&... us) : _instructions{std::forward<Us>(us)...} {}
template <std::size_t... Is> // extract by indices
InstructionSet<std::tuple_element_t<Is, std::tuple<Ts...>>...> extract();
template <class U> // extract all of a certain by type
auto extract() {
return [this]<std::size_t... Is>(std::index_sequence<Is...>) {
return extract<Is...>();
}(get_indices_for<U, Ts...>());
}
private:
std::tuple<Ts...> _instructions;
};
template <class... Ts>
InstructionSet(Ts&&...) -> InstructionSet<std::remove_cvref_t<Ts>...>;
template <class... Ts>
requires(is_instruction_type<Ts> && ...)
template <std::size_t... Is>
InstructionSet<std::tuple_element_t<Is, std::tuple<Ts...>>...>
InstructionSet<Ts...>::extract() {
return {std::get<Is>(_instructions)...};
}
评论
0赞
Irreducible Polynomial
11/12/2023
我以前从未将 std::get 用于类型,但是,如果我正确理解定义,此实现期望每条指令只有一个?而我想存储多个相同类型的文件,并返回所有相同类型的文件。
0赞
Irreducible Polynomial
11/12/2023
另外,另一件事是我希望它能够通用地存储在其他地方的数据结构中(所以我试图使用虚拟方法使它能够从指向 InstructionSet 的指针<>获取整个对象)
0赞
Ted Lyngmo
11/12/2023
@IrreduciblePolynomial好的,我现在已经更新了它,以便可以提取所有特定类型。我还用析构函数制作了一个基类,这样你就可以将基类指针存储在容器等中。extract
virtual
0赞
Igor Tandetnik
11/12/2023
#2
我想出了这个:
#include <tuple>
#include <type_traits>
namespace internal {
// The number of occurrences of U in Ts
template <typename U, typename... Ts>
constexpr std::size_t CountSameType() {
return (std::is_same_v<U, Ts> + ...);
}
// The index of Ith occurrence of U in {First, Rest...}
template <std::size_t I, typename U, typename First, typename ... Rest>
struct IthIndex : public std::integral_constant<std::size_t,
1 + IthIndex<I - std::is_same_v<U, First>, U, Rest...>::value>
{};
template <typename U, typename ... Rest>
struct IthIndex<0, U, U, Rest...> : public std::integral_constant<std::size_t, 0> {};
// An index_sequence mapping occurrences of U in Ts,
// given a sequence {0, 1, CountSameType<U, Ts...>}
template <typename U, typename... Ts, std::size_t... Is>
std::index_sequence<IthIndex<Is, U, Ts...>::value...>
SameTypeIndexes(std::index_sequence<Is...>);
// Produce a tuple that's a subset of the given tuple,
// containing only elements at given indexes.
template <typename Tuple, std::size_t ... Is>
auto ExtractFromTuple(const Tuple& t, std::index_sequence<Is...>) {
return std::make_tuple(std::get<Is>(t)...);
}
} // namespace internal
template <typename... Ts>
class InstructionSet;
template <typename... Ts>
auto MakeInstructionSet(const std::tuple<Ts...>& t) { return InstructionSet<Ts...>(t); }
template <typename... Ts>
class InstructionSet {
std::tuple<Ts...> instructions;
public:
InstructionSet(Ts&&... args) : instructions(args...) {}
InstructionSet(const std::tuple<Ts...>& t) : instructions(t) {}
template <typename U>
auto extract() {
constexpr std::size_t N = internal::CountSameType<U, Ts...>();
using SameTypeIndexes =
decltype(internal::SameTypeIndexes<U, Ts...>(std::make_index_sequence<N>{}));
return MakeInstructionSet(internal::ExtractFromTuple(instructions, SameTypeIndexes{}));
}
};
评论