c++ 理解SFINAE:部分专用类之外的成员函数声明,以及可变类中的模板

1wnzp6jl  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(80)

我想做的是:
创建一个“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的基本知识,对于带有类型定义和静态变量的类,但是对于更复杂的类,我感到困惑,似乎找不到很多例子。

kcugc4gi

kcugc4gi1#

我认为您可以简化它,不进行专门化,而是使用std::tuple存储指令。
首先使用一个helper函数来创建一个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)...};
}


Demo

l7wslrjt

l7wslrjt2#

我想到了这个:

#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{}));
  }
};

字符串
Demo

相关问题