编译时禁用字段定义

4
我想创建一个带/不带线程安全的池。如果池不是线程安全的,我不想定义互斥字段,因此我使用了std::conditional。然而,由于它没有完全做到我想要的,而且创建了两个“类型”选项,所以我选择了“int8(char)”作为被动化的互斥类型。(相反,我希望整个定义都消失)
template<typename T, bool threadSafe = true>
class Pool
{
private:
    //Mutex mutex; this is the field i want it to be DISAPPEARED, i modified it as below
    std::conditional<threadSafe, Mutex, int8>::type mutex;
protected:
    static constexpr item_type_size_datatype TypeSizeX = sizeof(T) + sizeof(size_t);
public:
    Pool(size_t clusterItemCount) : ClusterItemCount(clusterItemCount),
        ClusterByteSize(clusterItemCount* TypeSizeX)
    {
#ifdef CriticalSection
        if constexpr (threadSafe)
            InitializeCriticalSection(&mutex);
#endif
    }
    ~Pool()
    {
        Clear();

#ifdef CriticalSection
        if constexpr (threadSafe)
            DeleteCriticalSection(&mutex);
#endif
    }

    T* Occupy(bool& outFirstTime)
    {
        if constexpr (threadSafe)
        {
            MutexLock(mutex);
        }

        //do the occupation

        if constexpr (threadSafe)
        {
            MutexUnlock(mutex);
        }

        return result;
    }
};

正如您所看到的,在方法内部我使用了“constexpr if”,它的效果非常好,因为它可以禁用整个代码块。

主要问题:除了“std::conditional”之外,是否有更好的方法来禁用整个定义,例如“Mutex mutex;”?

附加问题:对于“int8 mutex”,我收到了“未初始化变量”的警告,我必须用“0”进行初始化。如何以“std::conditional”方式在编译时执行此操作。

3个回答

5

这可以通过模板特化来实现,例如:

template<bool threadSafe>
class PoolBase;

template<>
class PoolBase<false>
{// empty
};

template<>
class PoolBase<true>
{
   protected: Mutex m_mutex;
};

template<typename T, bool threadSafe = true>
class Pool: private PoolBase<threadSafe>
{
...


3
此外,如果将整个类专门化不切实际,可以将“可能存在,也可能不存在”的成员包装在以这种方式专门化的模板结构中。 - saxbophone
1
这个解决方案结合了两个习语:基于成员的基类空基类优化 - Red.Wave
这似乎是一个非常好的解决方案,但是当我想从[Pool]类访问[mutex]时,编译器会创建一个错误,错误为:"error C2065: 'mutex': undeclared identifier"。这几乎像是一个编译时问题,并且没有看到基本模板[mutex]字段并说它不存在。 - Ibrahim Ozdemir
@IbrahimOzdemir,你需要以this->m_mutex或者PoolBase<threadSafe>::m_mutex的方式访问它,因为它是一个依赖于模板参数的名称。请参考https://dev59.com/dHRB5IYBdhLWcg3weXOX。 - user7860670
@user7860670 嘿,谢谢伙计,这个方法可行。总的来说,这个解决方案非常棒,没有虚拟方法(vtable),轻巧高效,非常感谢。 - Ibrahim Ozdemir

0

另一种方法是将Pool作为基类,将线程安全的PoolSafe作为派生类,并进行必要的特化。例如:

template<typename T>
class Pool
{
protected:
    static constexpr item_type_size_datatype TypeSizeX = sizeof(T) + sizeof(size_t);
public:
    Pool(size_t clusterItemCount) : ClusterItemCount(clusterItemCount),
        ClusterByteSize(clusterItemCount* TypeSizeX)
    {
    }
    virtual ~Pool()
    {
        Clear();
    }
    virtual T* Occupy(bool& outFirstTime)
    {
        //do the occupation
        return result;
    }
};

template<typename T>
class PoolSafe: public Pool<T>
{
private:
    Mutex mutex;
public:
    PoolSafe(size_t clusterItemCount) : Pool<T>(clusterItemCount)
    {
        InitializeCriticalSection(&mutex);
    }
    virtual ~PoolSafe()
    {
        DeleteCriticalSection(&mutex);
    }
    virtual T* Occupy(bool& outFirstTime)
    {
        MutexLock(mutex);
        auto result = Pool<T>::Occupy(outFirstTime);
        MutexUnlock(mutex);
        return result;
    }
};

0

如其他答案所说,您可以使用模板特化来实现这一点,因为不幸的是,无法在成员声明中使用std :: enable_if

为了使您不需要对整个类进行特化,从而产生重复定义和声明,您可以创建一个容器结构体,该结构体根据您是否要包含变量而进行模板特化,然后将此模板化结构体用作成员:

template <bool include_the_thing = true>
struct inner {
    int thing{};
};
template<>
struct inner<false> {};

template <bool include_the_thing>
struct outer {
    inner<include_the_thing> maybe;
};

#include <cassert>

int main() {
    outer<true> has_thing;
    assert(has_thing.maybe.thing == 0);
    outer<false> doesnt_have_thing;
    // assert(doesnt_have_thing.maybe.thing == 0); // compiler error --maybe.thing doesn't exist
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接