如何使用另一个 class 作为 class 模板专业化

How to use another class as a class template specialization

我有一个混合锁 class,自旋尝试锁定一定数量的自旋(编译时间固定),然后回退到 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 的特殊情况下,这会退回到 'plain' std::mutex(即没有 visible 自旋)。

所以我将其专门用于:

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

它工作正常,但是将 class 模板专门化为另一个(预先存在的)模板的方式是否已获批准?

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

嗯,现在 hybird_lock<0>hybird_lock<1> 是完全不同的东西,一个来自 std::mutex,另一个来自 contains/wraps。这改变了 hybird_lock 的整个构成及其背后的含义。 IE。它们在语义上并不相同。这可能会导致一些相当意想不到的后果 -hybird_lock<0> 会继承很多其他情况不会有的东西。

如果这是唯一的区别,我根本不会为专业化而烦恼。请记住,zero-case 将在编译时已知,并且可以肯定的是,整个循环将被完全优化。

如果还有其他重要的(或实际的)优化,我会选择类似的东西:

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

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

没有 "official" 方法,但这里有一个好方法 - 对于模板,将主模板 class 分解为更小的 'action' 或'function' 模板 class 是的。通过这种方式,您可以更好地控制和细化专业化,这意味着您只需在一个地方维护主要逻辑:

#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;
}

Richard Hodges 的回答很好,但您可以简单地重载方法 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, from c++14, if your compiler doesn't support it you can use type alias type alias 而不是:

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

并像这样使用它

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