如何使用另一个类作为类模板特化

时间:2015-06-30 14:19:28

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

我有一个混合锁定类,它旋转尝试锁定一个(编译时固定的)旋转次数,然后再回到std::mutex上的阻塞,直到锁定变为可用。

简化为:

#include <mutex>

template<unsigned SPIN_LIMIT>
class hybrid_lock {
public:
    void lock(){
        for(unsigned i(0);i<SPIN_LIMIT;++i){
            if(this->mMutex.try_lock()){
                return;        
            }
        } 
        this->mMutex.lock();
    }
    void unlock(){
        this->mMutex.unlock();
    }
private:
    std::mutex mMutex;
};

SPIN_LIMIT==0的特殊情况下,这又回归为“普通”std::mutex(即没有可见旋转)。

所以我专注于:

template<>
class hybrid_lock<0> : public std::mutex {};

它工作得很好但是批准的专门化类模板的方法是另一个(预先存在的)模板吗?

3 个答案:

答案 0 :(得分:2)

注意:我回答了实际问题,而不是标题中的问题。

好吧,现在hybird_lock<0>hybird_lock<1>完全不同,一个派生自std::mutex,另一个派生/包装它。这改变了hybird_lock的整体构成及其背后的意义。即它们在语义上并不相同。这可能会导致一些意想不到的后果 - hybird_lock<0>会继承很多东西,而其他情况则不会。

如果这是唯一的区别,我根本不会打扰专业化。请记住,在编译时将知道零情况,并且当它完成时,整个循环将被完全优化。

如果还有其他重要(或实际)优化,我会选择以下内容:

template<>
class hybrid_lock<0> {
public:
    void lock(){
      this->mMutex.lock();
    }
    void unlock(){
      this->mMutex.unlock();
    }
private:
    std::mutex mMutex;
};

这种实现使0成为一种特殊情况,而不是几乎完全不同的东西。

答案 1 :(得分:1)

没有“官方”的方法,但这是一个好方法 - 使用模板,将主模板类分解为更小的“动作”或“功能”模板类通常更好。通过这种方式,您可以获得更多的控制和粒度而不是专业化,这意味着您只需要在一个地方维护主逻辑:

#include <iostream>
#include <mutex>

// general form of the spin_locker
template<unsigned SPIN_LIMIT, class Mutex>
struct spinner
{
    static void lock(Mutex& m) {
        for (unsigned i = 0 ; i < SPIN_LIMIT ; ++i)
            if (m.try_lock())
                return;
        m.lock();
    }
};

// optmised partial specialisation for zero spins
template<class Mutex>
struct spinner<0, Mutex>
{
    static void lock(Mutex& m) {
        m.lock();
    }

};

template<unsigned SPIN_LIMIT, class Mutex = std::mutex>
class hybrid_lock {

    using spinner_type = spinner<SPIN_LIMIT, Mutex>;

public:
    void lock(){
        spinner_type::lock(mMutex);
    }

    void unlock(){
        mMutex.unlock();
    }

    std::unique_lock<Mutex> make_lock() {
        return std::unique_lock<Mutex>(mMutex);
    }

private:
    Mutex mMutex;
};

// since only the 'spinner' functor object needs specialising there is now no need to specialise the main logic

using namespace std;

auto main() -> int
{
    hybrid_lock<100> m1;
    hybrid_lock<0> m2;
    hybrid_lock<100, std::recursive_mutex> m3;
    hybrid_lock<0, std::recursive_mutex> m4;

    auto l1 = m1.make_lock();
    auto l2 = m2.make_lock();
    auto l3 = m3.make_lock();
    auto l4 = m4.make_lock();

    return 0;
}

答案 2 :(得分:1)

理查德·霍奇斯的答案很棒,但你可以简单地重载方法lock

#include <iostream>
#include <type_traits>
#include <mutex>

template<class Mutex = std::mutex>
struct hybrid_lock {
    template<int N>
    void lock(std::integral_constant<int, N> val){
        for (unsigned i = 0 ; i < val() ; ++i)
            if (mMutex.try_lock())
                return;
        mMutex.lock();
    }

    void lock(std::integral_constant<int, 0>){
        mMutex.lock();
    }

    void unlock(){
        mMutex.unlock();
    }

    std::unique_lock<Mutex> make_lock() {
        return std::unique_lock<Mutex>(mMutex);
    }

private:
    Mutex mMutex;
};

template <int N>
constexpr
std::integral_constant<int, N> IC;

int main() {

    hybrid_lock<> m1;
    hybrid_lock<> m2;

    m1.lock(IC<0>);
    m2.lock(IC<100>);
    return 0;
}

IC是variable template,来自c ++ 14,如果你的编译器不支持它,你可以使用类型别名type alias代替:

template<int N>
using IC = std::integral_constant<int, N>;

并像这样使用

m.lock(IC<0>{});