在C++中创建"template"对象的机制

Mechanism to create "template" objects in C++

本文关键字:对象 机制 template 创建 C++      更新时间:2023-10-16

我希望能够用一些默认值初始化对象,但要从外部代码(不嵌入类本身(来实现这一点。对象暴露在外部编辑器中,我不想一次又一次地设置相同的值,只更改一些不同的值。由于我已经有了模板类,所以我想从"traits"类中完成这项工作。

这是我想要实现的一个简单的例子:

template<typename Traits>
class Test
{
    public:
        Test()
        {
            //if Traits has Init init function call Traits::Init(this)
        }
    private:
        typename Traits::Type value;
    friend Traits;
};
struct TestTraits
{
    typedef std::string Type;
};
struct TestTraitsInit
{
    typedef int Type;
    static void Init(Test<TestTraitsInit>* obj)
    {
        obj->value = 0;
    }
};
int main()
{
    Test<TestTraits> obj1;
    Test<TestTraitsInit> obj2;
}

正如您所看到的,只有在某些情况下才有Init()是有意义的是否可以检查类Traits是否有Init()函数,并仅在它存在时调用它

我知道一个非常简单的解决方案是使用空的Init()函数,但我想要一个更好的解决方案:(

您可以基于表达式SFINAE:创建一些具有适当的SFINAE约束专门化的类模板maybe_call_init

template<typename T, typename = void>
struct maybe_call_init
{
    static void maybe_call(Test<T>* obj) { }
};
template<typename T>
struct maybe_call_init<T,
    decltype(T::Init(std::declval<Test<T>*>()), void(0))>
{
    static void maybe_call(Test<T>* obj) { T::Init(obj); }
};

给定一个特性T,如果T定义了这样一个函数,maybe_call_init<T>::maybe_call(obj)将调用T::Init(obj),否则它将不执行任何操作。

然后,您可以在原始类模板中这样使用它:

template<typename Traits>
class Test
{
public:
    Test()
    {
        maybe_call_init<Traits>::maybe_call(this);
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    }
private:
    typename Traits::Type value;
    friend Traits;
};

上述解决方案有点初级,可以通过将maybe_call_init类模板及其专用化隐藏在detail命名空间中,提供一个助手函数来完成实例化工作来进行改进。因此,考虑到这台机器:

namespace detail
{
    template<typename T, typename U, typename = void>
    struct maybe_call_init
    {
        static void maybe_call(U* obj) { }
    };
    template<typename T, typename U>
    struct maybe_call_init<T, U,
        decltype(T::Init(std::declval<U*>()), void(0))>
    {
        static void maybe_call(U* obj) { T::Init(obj); }
    };
}
template<template<typename> class T, typename U>
void maybe_call_init(T<U>* obj)
{
     detail::maybe_call_init<U, T<U>>::maybe_call(obj);
}

您最初的Test类的构造函数现在可能如下所示:

template<typename Traits>
class Test
{
    public:
        Test()
        {
            maybe_call_init(this);
        //  ^^^^^^^^^^^^^^^^^^^^^
        }
    public:
        typename Traits::Type value;
    friend Traits;
};

这是一个的实际示例