C++ 模板化 - 可选构造函数

C++ Templated-Optional Constructors

提问人:Carson 提问时间:11/12/2023 更新时间:11/12/2023 访问量:51

问:

初步资料

我正在实现一个队列。我有一个看起来像这样的类:Vec

#include <cstddef>
#include <span>

template<typename T, size_t Extent, bool Dynamic = (Extent == std::dynamic_extent)>
class Vec {
public:
    constexpr size_t capacity(void) const;
    ~Vec();
};

template<typename T, size_t Extent>
class Vec<T, Extent, true> {
    // Dynamic version
public:
    Vec(size_t capacity);
};

template<typename T, size_t Extent>
class Vec<T, Extent, false> {
    // Statically-sized version of the class
public:
    constexpr Vec() = default;
};

换句话说,可以用 when Extent != std::d yanmic_extent 来构造。否则,必须传入大小,并且构造函数为 。这被设计为类似于 std::span 提供的 api。Vecconstexpr Vec()Vec(size_t capacity)

问题

我怎样才能把它包含在一个新类中,这样我就可以在两个类之间共享大部分功能,但仍然有专门的 ctor。例如,我想这样写:Queue

template<typename T, size_t Extent = std::dynamic_extent>
class Queue {
public:
    template</* only enable if Extent != std::dynamic_extent */>
    constexpr Queue() {}

    template</* only enable if Extent == std::dynamic_extent */>
    Queue(size_t size) : m_slots(size) {}

    // Other methods...
private:
    Vec<T, Extent> m_slots;
    // Other fields ...
};

我知道,我可以添加一个参数匹配,但是我需要重复每个类中的所有字段,并提供重复的函数定义。bool DynamicVec<T>

我尝试了几种不同的模板表达式变体(仅显示模板,表达式只是明显的反转)。constexpr Queue()Queue(size_t size)

尝试 1:未命名,默认参数,可选无效

template<typename = typename std::enable_if_t<Extent != std::dynamic_extent>>
constexpr Queue() {}

尝试 2:尝试模仿 std::enable_if 上的 c++ 文档

template<std::enable_if_t<Extent != std::dynamic_extent, bool> = true>

这两种尝试都编译失败,并显示警告:

/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/type_traits:2608:44: error: no type named 'type' in 'std::enable_if<false, bool>'; 'enable_if' cannot be used to disable this declaration
 2608 |     using enable_if_t = typename enable_if<_Cond, _Tp>::type;
      |                                            ^~~~~
../src/queues/spsc.hh:34:16: note: in instantiation of template type alias 'enable_if_t' requested here
   34 |         template<std::enable_if_t<Extent == std::dynamic_extent, bool> = true>
      |

如何实现此行为?有没有我没有看到的更好的模式?

链接

我已经在这个 godbolt 中重现了这个问题,我的两次尝试。

C++ 模板 类型特征

评论


答:

2赞 Remy Lebeau 11/12/2023 #1

如果你看一下 cpppreference 的 std::span 构造函数文档,你会看到它是如何在 C++20 中限制其某些构造函数的。仅供参考,它根本没有在模板参数中使用 SFINAE,而是使用新的 explicit(expression) 功能,例如:std::dynamic_extent

constexpr span() noexcept;

template< class It >
explicit(extent != std::dynamic_extent)
constexpr span( It first, size_type count );

template< class It, class End >
explicit(extent != std::dynamic_extent)
constexpr span( It first, End last );

template< std::size_t N >
constexpr span( std::type_identity_t<element_type> (&arr)[N] ) noexcept;

template< class U, std::size_t N >
constexpr span( std::array<U, N>& arr ) noexcept;

template< class U, std::size_t N >
constexpr span( const std::array<U, N>& arr ) noexcept;

template< class R >
explicit(extent != std::dynamic_extent)
constexpr span( R&& range );

template< class U, std::size_t N >
explicit(extent != std::dynamic_extent && N == std::dynamic_extent)
constexpr span( const std::span<U, N>& source ) noexcept;

constexpr span( const span& other ) noexcept = default;