我想要达到的目标
动机是我想要有表单的模板
template<typename T, typename ...Args>
其中
T
是某种类类型。如果 T
是 BaseA
或 BaseA
的任何派生类,我想要此模板的专用版本。
(我遵循的C++标准是C++20)
问题
如果第二个模板参数是确定的,这很容易实现:
template <typename T, typename U, typename Enable = void>
class Factory
{
public:
Factory(U arg)
{
m_spInner = std::make_shared<T>(arg);
}
void print() const
{
printf("Factory normal: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
template <typename T, typename U>
class Factory<T, U, typename std::enable_if<std::is_base_of<BaseA, T>::value>::type>
{
public:
Factory(U arg)
{
m_spInner = std::make_shared<T>(arg);
}
void print() const
{
printf("Factory BaseA: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
// callers
int main()
{
Factory<DerivedA1, int> factoryA1(1);
factoryA1.print();
Factory<DerivedA2, int> factoryA2(2);
factoryA2.print();
Factory<DerivedB, char> factoryB('B');
factoryB.print();
Factory<C, string> factoryC("C");
factoryC.print();
//Factory<int> factoryInt(1); // compile error
//factoryInt.print();
return 0;
}
完成工作。
但是当第二个参数不定时,就像
template <typename T, typename ...Args, typename Enable = void>
编译时不会出现错误消息:不定参数必须是最后一个参数。
如果我将
Enable
放在 ...Args
之前,模板本身可以编译,但调用不起作用,因为 int
不适合 Enable
模板参数。
所以我想知道是否可以使
enable_if
与具有不定参数的类模板一起使用。或者是否有其他优雅的方法来实现为类及其所有子类专门化模板的需要?
任何建议都非常受欢迎。非常感谢!
附录
为了方便起见,只需在下面发布我的测试代码即可。如果定义了
INDEFINITE_PARAM
,它将编译INDEFINITE_PARAM版本,这会导致错误,因为我不知道如何正确放置enable_if
。如果 INDEFINITE_PARAM
未定义,它可以编译确定的参数版本并给出预期结果。
#include <type_traits>
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <string>
//#define INDEFINITE_PARAM
using namespace std;
class BaseA
{
public:
BaseA(int id): m_id(id) {}
virtual void print() const
{
printf("BaseA\n");
}
private:
int m_id;
};
class BaseB
{
public:
BaseB(char id): m_id(id) {}
virtual void print() const
{
printf("BaseB\n");
}
private:
char m_id;
};
class DerivedA1 : public BaseA
{
public:
DerivedA1(int id) : BaseA(id) {}
void print() const override
{
printf("DerivedA1\n");
}
};
class DerivedA2 : public BaseA
{
public:
DerivedA2(int id) : BaseA(id) {}
void print() const override
{
printf("DerivedA2\n");
}
};
class DerivedB : public BaseB
{
public:
DerivedB(char id) : BaseB(id) {}
void print() const override
{
printf("DerivedB\n");
}
};
class C
{
public:
C(string id) : m_id(id) {}
void print() const
{
printf("C\n");
}
private:
string m_id;
};
#ifdef INDEFINITE_PARAM
template<typename T, typename Base, typename ...Args>
class Factory
{
public:
Factory(Args&&... args)
{
m_spInner = std::make_shared<T>(std::forward<Args>(args)...);
}
void print() const
{
printf("%s\n", typeid(T::Base).name());
printf("Factory normal: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
template <typename T, typename ...Args>
class Factory<T, BaseA, Args...>
{
public:
Factory(Args&&... args)
{
m_spInner = std::make_shared<T>(std::forward<Args>(args)...);
}
void print() const
{
printf("%s\n", typeid(T::Base).name());
printf("Factory BaseA: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
template <typename T, typename... Args>
class Factory<T, typename std::enable_if<std::is_base_of<BaseA, T>::value, BaseA>::type, Args...>
{
public:
Factory(Args&&... args)
{
m_spInner = std::make_shared<T>(std::forward<Args>(args)...);
}
void print() const
{
printf("%s", std::is_base_of<BaseA, T>::value);
printf("Factory BaseA: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
/*
template <typename T, typename... Args>
class Factory<T, void, Args...>
{
public:
Factory(Args&&... args)
{
m_spInner = std::make_shared<T>(std::forward<Args>(args)...);
}
void print() const
{
printf("Factory Normal: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
*/
#else
template <typename T, typename U, typename Enable = void>
class Factory
{
public:
Factory(U arg)
{
m_spInner = std::make_shared<T>(arg);
}
void print() const
{
printf("Factory normal: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
template <typename T, typename U>
class Factory<T, U, typename std::enable_if<std::is_base_of<BaseA, T>::value>::type>
{
public:
Factory(U arg)
{
m_spInner = std::make_shared<T>(arg);
}
void print() const
{
printf("Factory BaseA: ");
m_spInner->print();
}
private:
std::shared_ptr<T> m_spInner;
};
#endif
int main()
{
Factory<DerivedA1, int> factoryA1(1);
factoryA1.print();
Factory<DerivedA2, int> factoryA2(2);
factoryA2.print();
Factory<DerivedB, char> factoryB('B');
factoryB.print();
Factory<C, string> factoryC("C");
factoryC.print();
//Factory<int> factoryInt(1); // compile error
//factoryInt.print();
return 0;
}
从 C++20 开始,您不再需要 Enabler 模板参数和
std::enable_if
/std::void_t
(这确实不能与可变参数模板一起使用)。您可以使用归并:
template<typename T, typename ...Args>
class Factory
{
//...
};
template<typename T, typename ...Args>
requires(std::is_base_of<BaseA, T>::value)
class Factory
{
//...
};
注意:在您的情况下,额外的
Args
可能仅用于构造函数:
template <typename T>
requires(std::is_base_of<BaseA, T>::value)
class Factory<T>
{
public:
template <typename... Args>
Factory(Args&&... args)
{
m_spInner = std::make_shared<T>(std::forward<Args>(args)...);
}
// ...
};