C++ 根据类创建时的模板参数更改类API
我想创建一个类,在特定的模板实例化下,它将公开一个不同的API。它具有常见的函数,但如果用户将使用类的特定实例化,则应禁用一些函数。大概是这样的:C++ 根据类创建时的模板参数更改类API,c++,c++11,templates,sfinae,C++,C++11,Templates,Sfinae,我想创建一个类,在特定的模板实例化下,它将公开一个不同的API。它具有常见的函数,但如果用户将使用类的特定实例化,则应禁用一些函数。大概是这样的: VarApi<T1> v1; v1.common(); v1.funcA1(); // v1.funcA2(); // ERROR v1.funcA1_2(); VarApi<T2> v2; v1.common(); // v2.funcA1(); // ERROR v2.funcA2(); v2.funcA1_2();
VarApi<T1> v1;
v1.common();
v1.funcA1();
// v1.funcA2(); // ERROR
v1.funcA1_2();
VarApi<T2> v2;
v1.common();
// v2.funcA1(); // ERROR
v2.funcA2();
v2.funcA1_2();
VarApi<T3> v3;
v3.common();
// v2.funcA1(); // ERROR
// v2.funcA2(); // ERROR
// v1.funcA1_2(); // ERROR
enum Type { T1, T2, T3 };
template <Type TType> struct VarApi {
void common() { }
template <Type T = TType,
typename = typename std::enable_if<T == T1>::type>
void funcA1() { }
template <Type T = TType,
typename = typename std::enable_if<T == T2>::type >
void funcA2() { }
template <Type T = TType,
typename = typename std::enable_if<T == T1 || T == T2>::type >
void funcA1_2() { }
template <Type T = TType,
typename = typename std::enable_if<T == T3>::type >
void funcA3() { }
};
这可以工作并实现上述功能。问题在于,用户仍然可以通过以下方式覆盖此选项:
VarApi<T2> v2;
v2.funcA1<T1>(); // NOT ERROR
VarApi<T2> v2;
v2.funcA1<T1>(); // NOT ERROR
VarApi v2;
v2.funcA1();//不出错
有没有办法防止这种情况发生?您可以利用继承来提供所需的功能。使用CRTP,您可以通过
self
指针在func\u提供程序中访问原始类的功能
template<class T, class Derived> struct func_provider;
template<class Derived>
struct func_provider<int, Derived> {
void funcA1() {
auto self = static_cast<Derived*>(this);
// do something with self
}
};
template<class Derived> struct func_provider<double, Derived> { void funcA2() {} };
template<class T>
struct foo : public func_provider<T, foo<T>> {};
int main() {
foo<int> f;
foo<double> g;
f.funcA1();
// f.funcA2(); // Error
g.funcA2();
// g.funcA1(); // Error
}
模板结构函数提供程序;
模板
结构函数提供程序{
void funcA1(){
自动自=静态施法(本);
//自作主张
}
};
模板结构func_提供程序{void funcA2(){};
模板
struct foo:public func_provider{};
int main(){
福福;
傅g,;
f、 funcA1();
//f.funcA2();//错误
g、 funcA2();
//g.funcA1();//错误
}
编辑:
此版本允许用户在一个位置实现多种类型的功能,用户可以将各种类型组合在一起:
template<class... Ts> struct types {};
template<class Types, class T> struct is_in : public std::false_type {};
template<class... Ts, class T>
struct is_in<types<T, Ts...>, T> : public std::true_type {};
template<class... Ts, class T0, class T>
struct is_in<types<T0, Ts...>, T> : public is_in<types<Ts...>, T> {};
template<class Derived, bool B, class T> struct func_provider {};
template<class Derived, class T, class... Ts>
struct func_collector
: public func_provider<Derived, is_in<Ts, T>::value, Ts>...
{};
// implement functions for int
template<class Derived>
struct func_provider<Derived, true, types<int>> {
void funcA1() {
auto self = static_cast<Derived*>(this);
// do something with self
}
};
// implement functions for double
template<class Derived>
struct func_provider<Derived, true, types<double>> { void funcA2() {} };
// implement functions for both int and double
template<class Derived>
struct func_provider<Derived, true, types<int, double>> { void funcA1_2() {} };
template<class T>
struct foo : public func_collector<foo<T>, T,
// pull desired functions
types<int>, types<double>, types<int, double>>
{
void common() {}
};
int main() {
foo<int> f;
foo<double> g;
f.common();
f.funcA1();
f.funcA1_2();
// f.funcA2(); // Error
g.funcA2();
g.funcA1_2();
// g.funcA1(); // Error
}
模板结构类型{};
模板结构是_in:public std::false_type{};
模板
结构是_in:public std::true_type{};
模板
struct is_in:public is_in{};
模板结构函数提供程序{};
模板
结构函数收集器
:公共函数提供程序。。。
{};
//为int实现函数
模板
结构函数提供程序{
void funcA1(){
自动自=静态施法(本);
//自作主张
}
};
//实现双线程的功能
模板
结构func_提供程序{void funcA2(){};
//实现int和double函数
模板
结构函数提供程序{void funcA1_2(){};
模板
struct foo:公共函数收集器
{
void common(){}
};
int main(){
福福;
傅g,;
f、 普通();
f、 funcA1();
f、 函数1_2();
//f.funcA2();//错误
g、 funcA2();
g、 函数1_2();
//g.funcA1();//错误
}
解决方案1
实现所需功能的一种方法是使用tempalte专门化和依赖基类来提供可选功能
// I'm using E for enum. I find TType a bit misleading, since T usually stands for Type
template< Type EType >
struct VarApiBase { }; // empty by default
template< >
struct VarApiBase<T1> {
void funcA1() { }
};
template< >
struct VarApiBase<T2> {
void funcA2() { }
};
template <Type TType>
struct VarApi : VarApiBase<TType> {
void funcA1_2() { }
};
template <>
struct VarApi<T3> { };
如果确实需要SFINAE,仍然可以将==T1 | |==T2
条件放在模板中
template <Type ETypeInner = EType,
typename = typename std::enable_if<ETypeInner == T1 || ETypeInner == T2>::type >
void funcA1_2() {
static_assert(ETypeInner==EType);
}
例如,如果您有一个funcA2_3,您仍然可以这样做:
struct VarApiCommon {
void common();
};
struct VarApi12 : virtual VarApiCommon {
void funcA1_2();
};
struct VarApi23 : virtual VarApiCommon {
void funcA2_3();
};
template< Type EType >
struct VarApi; // undefined
template<>
struct VarApi<T1> : VarApi12 {
void funcA1();
};
template<>
struct VarApi<T2> : VarApi12, VarApi23 {
void funcA2();
};
template<>
struct VarApi<T2> : VarApi23 {
void funcA3();
};
struct varapicomon{
无效公共();
};
结构VarApi12:虚拟varapicomon{
void funcA1_2();
};
结构VarApi23:虚拟varapicomon{
void funcA2_3();
};
模板
结构VarApi;//未定义
模板
结构VarApi:VarApi12{
void funcA1();
};
模板
结构VarApi:VarApi12,VarApi23{
void funcA2();
};
模板
结构VarApi:VarApi23{
void funcA3();
};
这在很大程度上取决于成员
这可以工作并实现上述功能。问题在于,用户仍然可以通过以下方式覆盖此选项:
VarApi<T2> v2;
v2.funcA1<T1>(); // NOT ERROR
VarApi<T2> v2;
v2.funcA1<T1>(); // NOT ERROR
这可以防止模板“劫持”。我的建议是基于您能够提供实现,但希望隐藏它
有一个基本的实现,它实现了一切
template <class X> class Base
{
public:
void A();
void B();
void C();
void D();
void E();
};
模板类基类
{
公众:
使A()无效;
无效B();
无效C();
无效D();
无效E();
};
拥有一个派生类,该派生类继承受保护的,但随后从基中发布所有公共方法
template <class X> class Mid: protected Base<X>
{
public:
using Base::A;
using Base::B;
using Base::C;
// D & E are contentious
};
模板类Mid:受保护的基础
{
公众:
使用Base::A;
使用Base::B;
使用Base::C;
//D&E是有争议的
};
具有实际发布的类别,其中每个变体T1、T2、T3都是专门的。
这些类都是从第二个类公开继承的,但是publicfriend会发布它们确实支持的方法
template <class X> class Top: public Mid<X> {};
template <> class Top<X1>: public Mid<X1>
{
public:
using Base::D;
// Can't get E
};
template <> class Top<X2>: public Mid<X2>
{
public:
// Can't get D
using Base::E;
};
模板类Top:public Mid{};
模板类Top:公共Mid
{
公众:
使用Base::D;
//得不到E
};
模板类Top:公共Mid
{
公众:
//不可能
使用Base::E;
};
增益:无法访问要隐藏的方法。没有模板函数魔术
损失:发布的规则是任意的,根本不是由“可读”的结局驱动的。您也不能轻易地使用继承来构建规则,尽管您可能可以使用LikeX第二个模板参数。>“>但是我的结构是虚拟的。”不,这里没有虚拟的。@FantasticMrFox如果您的结构是虚拟的,请编辑问题以反映问题的实际复杂性。否则,我们就试着解决你告诉我们的问题。示例必须是最少的,但必须是完整的。@Jean-MichaëlCelerier如果我想共享公共功能,就会有,除非我在每个专门化中都重新实现了所有功能…@FantasticMrFox不太可能。您可以使用友元函数或基类。将公共功能放在func\u提供程序\u base
中,并使foo
或所有func\u提供程序
从中继承,这样就不会有任何虚拟。完美。这么简单。
template <class X> class Base
{
public:
void A();
void B();
void C();
void D();
void E();
};
template <class X> class Mid: protected Base<X>
{
public:
using Base::A;
using Base::B;
using Base::C;
// D & E are contentious
};
template <class X> class Top: public Mid<X> {};
template <> class Top<X1>: public Mid<X1>
{
public:
using Base::D;
// Can't get E
};
template <> class Top<X2>: public Mid<X2>
{
public:
// Can't get D
using Base::E;
};