【问题标题】:Specialize template based on *inner* type of a smart pointer基于 *inner* 类型的智能指针专门化模板
【发布时间】:2020-05-03 06:27:55
【问题描述】:

我有一个包含通用“智能指针”的类(可以是unique_ptrshared_ptr 等)。

我正在尝试专门化构造函数以调用适当的make_uniquemake_shared 等,但我不确定正确的语法。

这是想法(live link):

template <class SmartPtr>
struct Holder {

    typedef typename SmartPtr::element_type value_type;

    Holder()
        : m_ptr(new value_type) // The fallback implementation calls 'new' directly.
    {}

private:

    SmartPtr m_ptr;

};

// THIS DOES NOT COMPILE
// Notice the "T" type, which is not declared anywhere.
// How do I declare this sort of thing properly?
template<>
Holder<shared_ptr<T>>::Holder
    : m_ptr(make_shared<T>())
{}

希望这很清楚。 我想专门基于智能指针,但我也需要访问智能指针管理的底层类型。

这是 C++14,但我也对其他标准的想法感兴趣。


编辑:一些类似的问题,但在这种情况下并不完全适用(或者如果适用,我不清楚所需的转换)。

【问题讨论】:

    标签: c++ templates c++14 template-specialization


    【解决方案1】:

    忽略语法错误,您的代码未编译的原因是因为您不能部分特化函数模板,您只能部分特化类模板。但是,您可以重载函数。使用函数重载解决这个问题一点也不容易。我在下面详细介绍了两种技术,您可以使用它们来解决您的问题,其中第二种利用部分模板专业化。

    首先,假设你有一个C++17编译器,你可以使用if constexpr和模板编程来获得想要的效果:

    template <class SmartPtr>
    struct Holder {
    
        //this is a nested helper struct, which will allow us to deduce at compile-time
        //    whether SmartPtr is a std::unique_ptr or not, using function overloading
        template<typename T>
        struct is_unique_ptr{
            template<typename TT>
            constexpr static std::true_type test(std::unique_ptr<TT>*);
    
            constexpr static std::false_type test(...);
    
            using type = decltype(test(std::declval<T*>()));
            constexpr static bool value = type::value;
        };
    
    
        //this is a nested helper struct, which will allow us to deduce at compile-time
        //    whether SmartPtr is a std::shared_ptr or not, using function overloading
        template<typename T>
        struct is_shared_ptr{
            template<typename TT>
            constexpr static std::true_type test(std::shared_ptr<TT>*);
    
            constexpr static std::false_type test(...);
    
            using type = decltype(test(std::declval<T*>()));
            constexpr static bool value = type::value;
        };
    
        typedef typename SmartPtr::element_type value_type;
    
        //default constructor will conditionally construct m_ptr depending on its type
        Holder(){ 
            if constexpr(is_unique_ptr<SmartPtr>::value){
                m_ptr = std::make_unique<value_type>();
            } else if constexpr(is_shared_ptr<SmartPtr>::value){
                m_ptr = std::make_shared<value_type>();
            } else {
                m_ptr = new value_type{};
            }
        }
    
    private:
    
        SmartPtr m_ptr;
    
    };
    

    此解决方案不使用部分模板特化。但是,如果您坚持使用部分模板特化,或者您没有 c++17 编译器,则必须针对不同的指针类型对整个类进行特化:

    //generic class Holder (identical to your initial implementation)
    template <class SmartPtr>
    struct Holder {
    
        typedef typename SmartPtr::element_type value_type;
    
        Holder()
            : m_ptr(new value_type){
        }
    
    private:
    
        SmartPtr m_ptr;
    
    };
    
    //this partial specialisation will be selected if template parameter is a std::unique_ptr 
    template<typename T>
    struct Holder<std::unique_ptr<T>>{
        using value_type = T;
    
        Holder() : m_ptr(std::make_unique<value_type>()){
    
        }
    
        private:
            std::unique_ptr<T> m_ptr;
    
    };
    
    //this partial specialisation will be selected if template parameter is a std::unique_ptr 
    template<typename T>
    struct Holder<std::shared_ptr<T>>{
        using value_type = T;
    
        Holder() : m_ptr(std::make_shared<value_type>()) {
        }
    
        private:
            std::shared_ptr<T> m_ptr;
    };
    

    像这样使用部分模板特化会导致代码膨胀。如果您决定使用此解决方案,您可能需要考虑将常用方法分解到一个基类中,所有特化都从该基类继承。这将有助于减少代码膨胀。

    【讨论】:

      【解决方案2】:

      我想我想通了 (live link):

      关键是要有一个带有enable_if 的模板化构造函数来确定它是否是一个有效的选择。

      template <class SmartPtr>
      struct Holder {
      
          typedef typename SmartPtr::element_type value_type;
      
          Holder()
              : m_ptr(new value_type)
          {}
      
          template <class T = value_type>
          Holder(std::enable_if_t<  std::is_same<shared_ptr<T>,SmartPtr>::value  >* = 0)
              : m_ptr(make_shared<T>())
          {}
      
      private:
      
          SmartPtr m_ptr;
      

      };

      可能有更好的方法——is_same 检查似乎有点老套。但现在已经足够了。

      【讨论】:

        猜你喜欢
        • 2014-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-13
        • 1970-01-01
        • 1970-01-01
        • 2015-08-12
        • 2015-04-22
        相关资源
        最近更新 更多