【问题标题】:Is it possible to treat a pointer as an integral type in non-type template parameters?是否可以将指针视为非类型模板参数中的整数类型?
【发布时间】:2018-03-03 10:12:23
【问题描述】:

我正在尝试创建一个可以将默认值作为非类型参数传递的模板。

从编译器的角度来看,原始类型(WINAPI 的HANDLE)是指针类型,但从用户的角度来看,它被视为整数类型。

// Somewhere in system headers
#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
typedef void *HANDLE;

// My code
template<typename Handle, Handle Default>
class HandleWrapper
{
    ...
};

HandleWrapper<HANDLE, INVALID_HANDLE_VALUE>; // error: invalid nontype template argument of type `HANDLE`

我目前的解决方法

template<typename Handle, uintptr_t Default>
class HandleWrapper
{
    static_assert(std::is_pointer<Handle>::value, "Handle must be a pointer");
    static constexpr Handle DefaultHandle = reinterpret_cast<Handle>(Invalid);
};

我想正确的解决方案是以某种方式指定 typename Handle 应该被视为一个整数类型 (uintptr_t),只要对话没有缩小。

【问题讨论】:

  • 你的例子能编译吗?重新解释转换不是常量表达式。
  • 我真的不明白你为什么需要一个模板。
  • @KerrekSB reinterpret_cast 是与static_cast 不同的编译器指令。
  • @NeilButterworth 模板是必需的,因为不同的句柄可能有不同的默认值,它并不总是 -1。
  • 但想必它总是一个整数?在这种情况下,我不明白为什么不能使用非模板构造函数。

标签: c++ templates


【解决方案1】:

在我的代码中,我使用了一个模板化的句柄包装类,但我使用它的方法与您使用的方法不同。我为要包装的每种句柄类型定义了一个单独的traits 类,然后使用模板参数指定要在包装器中使用哪些特征。

尝试在您的情况下做类似的事情,例如:

struct InvalidHandleTraits
{
    using HandleType = HANDLE;
    static constexpr HANDLE DefaultHandle = INVALID_HANDLE_VALUE;
    //...
    static void Close(HANDLE h) { CloseHandle(h); }
    //...
};

struct NullHandleTraits
{
    using HandleType = HANDLE;
    static constexpr HANDLE DefaultHandle = NULL;
    //...
    static void Close(HANDLE h) { CloseHandle(h); }
    //...
};

... other traits as needed...

template<typename traits = InvalidHandleTraits>
class HandleWrapper
{
public:
    using HandleType = typename traits::HandleType;

    HandleWrapper(HandleType h = traits::DefaultHandle) : m_handle(h) { std::cout << "constructor: " << h << std::endl; }
    ~HandleWrapper() { traits::Close(m_handle); }
    //...

    operator HandleType() { return m_handle; }

private:
    HandleType m_handle;
};

然后你可以在需要的时候使用想要的traits类,例如:

HandleWrapper<> h = CreateFile(...);
// or:
// HandleWrapper<InvalidHandleTraits> h = CreateFile(...);

HandleWrapper<NullHandleTraits> h = CreateFileMapping(...);

Live Demo

【讨论】:

  • 我明白我的抽象并不理想,而类似特征的方法确实更好。但是,我仍然想知道是否有实用方法或至少需要编译器将指针视为整数的技术。
  • @Kentzo 指针不是整数类型。使编译器将指针视为整数的唯一方法是对其进行类型转换
  • @DanielStevens 显然是一个已知的 gcc 错误:gcc.gnu.org/bugzilla/show_bug.cgi?id=49171,但表示它已在 8.1 中修复。另见stackoverflow.com/questions/24398102
  • @DanielStevens 在我的情况下,对于较旧的编译器,我将模板参数保留为实际类型,并将 DefaultHandle 设为我在分配和比较中类型转换的 uintptr_t
  • 我发现的另一种解决方法是在特征结构中使用一个函数:static HANDLE NullValue() { return INVALID_HANDLE_VALUE; }。与静态数据声明不同,模板结构中的函数不需要是 constexpr。如果优化甚至开启到最低级别,函数调用将被内联,产生与static constexpr 数据成员一致的结果。并且不需要铸造。
猜你喜欢
  • 2016-08-23
  • 1970-01-01
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多