我有一个高度可配置的类,有许多模板参数,如下所示:
template<bool OptionA = false,bool OptionB = false,bool OptionC = false,class T = Class1B> class MyClass { }
现在,如果我想创建类类型并且我只想将OptionB设置为true,我必须执行以下操作:
MyClass< false,true>
特别是对于许多模板参数,这变得很麻烦.
不,我的问题是,是否有任何可用于使用构建器模式创建基于模板的类类型的示例?
我正在寻找这样的东西:
class Builder { uSEOptionA(); uSEOptionB(); uSEOptionC(); useClass2B(); //instead of Class1B create(); }
最后,调用Builder.uSEOptionB().uSEOptionC().useClass2B.create()应该返回MyClass< false,true,Class2B>.这可能吗?
编辑:向模板参数列表添加了类.
解决方法
正如其他人所说,做你想做的最简单的方法是使用enum而不是Builder.但是,如果你想要一个Builder,你可以尝试这样的事情:
template<bool OptionA = false,typename T = Class1B> struct Builder_t { Builder_t() = default; // ~Builder_t() { std::cout << "Builder dtor." << std::endl; } auto uSEOptionA() -> Builder_t<true,OptionB,OptionC,T> { return {}; } auto uSEOptionB() -> Builder_t<OptionA,T> { return {}; } auto uSEOptionC() -> Builder_t<OptionA,T> { return {}; } auto useClass2B() -> Builder_t<OptionA,Class2B> { return {}; } MyClass<OptionA,T> create() { return {}; } }; using Builder = Builder_t<>; // ... // Build MyClass<true,false,Class2B>: auto ma2 = Builder{}.uSEOptionA().useClass2B().create();
这会导致每个函数返回一个不同的Builder,其模板将被下一个函数使用;最终模板用作MyClass的模板.每个函数都修改其指定的模板参数,允许编译时版本的Builder模式.但是,它确实有成本,如果取消注释用户定义的析构函数,这一点就变得很明显了.
考虑这个简单的测试程序:
#include <iostream> #include <typeinfo> class Class1B {}; class Class2B {}; template<bool OptionA = false,typename T = Class1B> class MyClass { public: MyClass() { std::cout << "MyClass<" << OptionA << "," << OptionB << "," << OptionC << "," << "type " << typeid(T).name() << ">" << std::endl; } }; template<bool OptionA = false,T> create() { return {}; } }; using Builder = Builder_t<>; int main() { std::cout << std::boolalpha; std::cout << "Default:\n"; std::cout << "Direct: "; MyClass<> m; std::cout << "Builder: "; auto mdefault = Builder{}.create(); std::cout << std::endl; std::cout << "Builder pattern:\n"; std::cout << "A: "; auto ma = Builder{}.uSEOptionA().create(); std::cout << "C: "; auto mc = Builder{}.uSEOptionC().create(); std::cout << "---\n"; std::cout << "AB: "; auto mab = Builder{}.uSEOptionA().uSEOptionB().create(); std::cout << "B2: "; auto mb2 = Builder{}.uSEOptionB().useClass2B().create(); std::cout << "---\n"; std::cout << "ABC: "; auto mabc = Builder{}.uSEOptionA().uSEOptionB().uSEOptionC().create(); std::cout << "AC2: "; auto mac2 = Builder{}.uSEOptionA().uSEOptionC().useClass2B().create(); std::cout << "---\n"; std::cout << "ABC2: "; auto mabc2 = Builder{}.uSEOptionA().uSEOptionB().uSEOptionC().useClass2B().create(); }
通常,输出如下(使用GCC):
Default: Direct: MyClass<false,type 7Class1B> Builder: MyClass<false,type 7Class1B> Builder pattern: A: MyClass<true,type 7Class1B> C: MyClass<false,type 7Class1B> --- AB: MyClass<true,type 7Class1B> B2: MyClass<false,type 7Class2B> --- ABC: MyClass<true,type 7Class1B> AC2: MyClass<true,type 7Class2B> --- ABC2: MyClass<true,type 7Class2B>
但是,如果我们取消注释析构函数……
Default: Direct: MyClass<false,type 7Class1B> Builder dtor. Builder pattern: A: MyClass<true,type 7Class1B> Builder dtor. Builder dtor. C: MyClass<false,type 7Class1B> Builder dtor. Builder dtor. --- AB: MyClass<true,type 7Class1B> Builder dtor. Builder dtor. Builder dtor. B2: MyClass<false,type 7Class2B> Builder dtor. Builder dtor. Builder dtor. --- ABC: MyClass<true,type 7Class1B> Builder dtor. Builder dtor. Builder dtor. Builder dtor. AC2: MyClass<true,type 7Class2B> Builder dtor. Builder dtor. Builder dtor. Builder dtor. --- ABC2: MyClass<true,type 7Class2B> Builder dtor. Builder dtor. Builder dtor. Builder dtor. Builder dtor.
Builder_t :: create()之前的每个调用都会创建一个不同的Builder_t,所有这些都会在创建实例后被销毁.这可以通过使Builder_t成为constexpr类来减轻,但如果有大量参数要处理,则可能会减慢编译速度:
template<bool OptionA = false,typename T = Class1B> struct Builder_t { // Uncomment if you want to guarantee that your compiler treats Builder_t as constexpr. // size_t CompTimeTest; constexpr Builder_t() // Uncomment if you want to guarantee that your compiler treats Builder_t as constexpr. // : CompTimeTest((OptionA ? 1 : 0) + // (OptionB ? 2 : 0) + // (OptionC ? 4 : 0) + // (std::is_same<T,Class2B>{} ? 8 : 0)) {} constexpr auto uSEOptionA() -> Builder_t<true,T> { return {}; } constexpr auto uSEOptionB() -> Builder_t<OptionA,T> { return {}; } constexpr auto uSEOptionC() -> Builder_t<OptionA,T> { return {}; } constexpr auto useClass2B() -> Builder_t<OptionA,Class2B> { return {}; } constexpr MyClass<OptionA,T> create() { return {}; } }; using Builder = Builder_t<>; // .... // Uncomment if you want to guarantee that your compiler treats Builder_t as constexpr. // char arr[Builder{}/*.uSEOptionA()/*.uSEOptionB()/*.uSEOptionC()/*.useClass2B()/**/.CompTimeTest]; // std::cout << sizeof(arr) << '\n';