如何使用另一个 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>{});
我有一个混合锁 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>{});