默认类型和条件的模板类

4

我想在MyClass中使用std :: enable_if,以仅接受(uint32_t | uint64_t)类型,同时如果用户未提供任何类型,则根据下面的条件选择默认类型。

但我无法使其正常工作。(C ++ 17)

#include <vector>
#include <cstdint>

template <typename T=std::conditional_t<sizeof(void*) == 8, std::uint64_t, std::uint32_t>>
class MyClass
{
  private:
    std::vector<T> vs;
  public:
  // ...
};


int main(){
  MyClass a; // OK, the defaut type is used either uint32_t or uint64_t
  MyClass<std::uint32_t> b; // Ok, use the user provided type
  MyClass<long> c; // must not compile, T is not one of uint32_t, uint64_t
}

如果指针的大小更小,你会怎么做?虽然它们越来越少了,但仍有一些16位的µC存在...(当然,uint32_t也适用于这些情况)。 - Aconcagua
1
我个人更喜欢测试 sizeof(void*) == sizeof(uint64_t)。虽然罕见,但一些专用机器即使今天仍然具有 CHAR_BIT == 16... - Aconcagua
限制类型宽度的实际原因是什么?也许像 std::is_integral<T> && std::is_unsigned<T> 这样的方式更可取?std::uintptr_t 可以作为一个很好的默认值,如果系统/编译器提供了128位无符号整数,用户甚至可以使用它... - Aconcagua
2个回答

5

您可以添加static_assert来执行检查。

template <typename T=std::conditional_t<sizeof(void*) == 8, std::uint64_t, std::uint32_t>>
class MyClass
{
  static_assert(std::is_same_v<T, std::uint64_t> || std::is_same_v<T, std::uint32_t>, "T must be std::uint64_t or std::uint32_t");
  private:
    std::vector<T> vs;
  public:
  // ...
};

LIVE


0

songyanyao 提出的使用 static_assert 的解决方案很好,完全有效,并且比我下面提出的更短。然而...

还有另一种选择,让编译器在不需要 static_assert 的情况下对您进行检查:

template <typename T>
class MyClassBase {
    std::vector<T> vs;
  public:
  // ...
};

template <typename T =
            std::conditional_t<sizeof(void*) == 8, std::uint64_t, std::uint32_t>>
class MyClass;

template <>
class MyClass<std::uint64_t>: public MyClassBase<std::uint64_t> {};

template <>
class MyClass<std::uint32_t>: public MyClassBase<std::uint32_t> {};

http://coliru.stacked-crooked.com/a/66033e773f001379


如果由于某种原因没有static_assert,那就太好了。好吧,std::conditional 也可能不可用,但自己实现很容易。缺点是:必须维护两个实现。 - Aconcagua
static_assert 表示该类型_可以被创建_,但我们通过 static_assert 在代码的某处 禁止它。假设其他编译单元的某个人添加了显式特化 template <> class MyClass<long> {};。有人可能会认为 static_assert 禁止它,但实际上并不是这样。按照我的方法,除非你创建它,否则该类型就不存在。没有魔法。但是,两种方式都是有效的。 - Amir Kirsh
那么实际上有什么不同呢?这两种方法都允许只使用所需的两种类型模板,这两种方法也都允许通过添加另一种特殊化来规避(所需的!)限制。我很清楚它们背后的技术差异,但这不是我的重点 - 而是当你被迫编译C ++03或更早版本时,你也可以使用你的方法,而另一种方法则不行。即使在C++11推出近十年之后,这样的限制仍然存在... - Aconcagua

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接