【问题标题】:How to restrict template class to only specific specializations with type aliases如何将模板类限制为仅具有类型别名的特定专业化
【发布时间】:2017-03-02 13:11:47
【问题描述】:

我的目标是拥有一个具有各种别名的模板化 Wrapper 类。这是一个简化的预览:

template < typename Type >
class Wrapper {
public:
   Wrapper(Type* resource) : ptr(resource) {}
   ~Wrapper() { free(ptr); }

private:
   Type* ptr;
}

void free(SDL_Window* ptr) { SDL_DestroyWindow(ptr); }
void free(SDL_Renderer* ptr) { SDL_DestroyRenderer(ptr); }

using Window = Wrapper<SDL_Window>;
using Renderer = Wrapper<SDL_Renderer>;

我希望只允许创建 Wrapper 类的这些别名实例。原因之一是它是 SDL 资源指针的包装器,它具有不同的内存释放功能,具体取决于指针的类型。

我想实现的最佳方案是使 Wrapper 类在我创建的别名的使用之外不可见。也许有一个使用匿名命名空间的解决方案,但是解冻意味着包装类不能在头文件中。

【问题讨论】:

  • 要么放置一个带有用户友好消息的静态断言,要么在实现文件中定义类模板的成员函数,并且只为类型的首选子集隐式实例化
  • 我也尝试过 static_assert 但我收到一个错误,即 typeid(Type) == typeid(SDL_Window) 不是常量表达式
  • 你需要std::is_same&lt;Type, SDL_Window&gt;::value,或者使用一些模板机制,比如 boost mpl list 或者你的自定义实现
  • 好的,用 is_same 语句填充的 static_assert 可以工作。还有没有办法完全隐藏 Wrapper,而只看到定义的别名?
  • 您至少需要将声明放在头文件中,并显式实例化模板类

标签: c++ templates c++14 anonymous-class


【解决方案1】:

我希望实现的最佳方案是使 Wrapper 类在我创建的别名之外不可见。

这可以使用private 和包装类来实现

class WrapperAccessor {
    template < typename Type >
    class Wrapper {
    public:
       Wrapper(Type* resource) : ptr(resource) {}
       ~Wrapper() { free(ptr); }

    private:
       Type* ptr;
    };

public:
    using Window = Wrapper<SDL_Window>;
    using Renderer = Wrapper<SDL_Renderer>;
};

using Window = WrapperAccessor::Window;
using Renderer = WrapperAccessor::Renderer;

【讨论】:

  • 谢谢,这正是我想要的。
【解决方案2】:

std::enable_if 怎么样,它只会为您的类/函数启用某些类型?

看看 C++11 和 14 中的type_traits。您可以进行各种静态检查(编译时检查)。例如,要检查类型是否符合您的预期,您可以使用:

std::is_same<T,int>::value

如果Tint,这将在编译时返回true

【讨论】:

  • 我可能会使用类似的方法来检查允许的类型。
  • 所以我已经尝试过了,指定了它应该允许的所有类型,然后创建了一个 Wrapper。即使根本不允许 int 特化,它也编译并运行,也不知道它的析构函数中发生了什么,因为 free(int) 重载不存在。
  • @MarošBeťko 如果您不希望它编译,请使用static_if,而不是if。以下是在 C++11 中实现它的方法:stackoverflow.com/questions/37617677/…
  • @MarošBeťko 另外,如果你使用std::enable_if,你可以防止编译你不想要的类型。但是enable_if 有一些陡峭的学习曲线,你必须做对。
【解决方案3】:

我认为这会有所帮助

template <typename T, bool Allowed>
class WrapperBase;


template <typename T>
class WrapperBase<T, true>
{
};

template <typename T>
class WrapperBase<T, false>;

template < typename Type >
class WrapperBaseHelper : public WrapperBase<T, boost::is_base_of<SDL_Window, T>::value | boost::is_base_of<SDL_Renderer>::value ...e.t.c >
{
...
};

【讨论】:

    【解决方案4】:

    在您的具体情况下,我会使用std::unique_ptr,例如:

    template <typename T, T value> struct Call; // c++17 should allow Call<auto F>
    
    template <typename T, void (*f)(T*)>
    struct Call<void (*)(T*), f>
    {
        void operator ()(T* p) const { f(p); }
    };
    
    #define AUTO(F) decltype(F), F // not needed in C++17
    
    using Window = std::unique_ptr<SDL_Window, Call<AUTO(&SDL_DestroyWindow)>>;
    using Renderer = std::unique_ptr<SDL_Renderer, Call<AUTO(&SDL_DestroyRenderer)>>;
    

    处理已删除的副本并纠正与您的 sn-p 相反的移动语义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多