在流畅接口中避免不必要的模板实例化
Avoiding unnecessary template instantiations in a fluent interface
我有一个带有一些可选模板参数的类:
struct option1_default_t {};
struct option2_default_t {};
template <typename T,
typename option1_t = option1_default_t,
typename option2_t = option2_default_t>
class foo
{
public:
foo(option1_t option1 = option1_t(), option2_t option2 = option2_t());
void run();
};
以及以下用于指定它们的流畅接口:
template <typename T, typename option1_t, typename option2_t>
struct foo_runner
{
option1_t option1_;
option2_t option2_;
template <typename new_option1_t>
foo_runner<T, new_option1_t, option2_t> option1(new_option1_t new_option1)
{
return foo_runner<T, new_option1_t, option2_t>{new_option1, option2_};
}
template <typename new_option2_t>
foo_runner<T, option1_t, new_option2_t> option2(new_option2_t new_option2)
{
return foo_runner<T, option1_t, new_option2_t>{option1_, new_option2};
}
void run()
{
foo<T, option1_t, option2_t> f(option1_, option2_);
f.run();
}
};
template <typename T>
foo_runner<T, option1_default_t, option2_default_t> make_foo()
{
return foo_runner<T, option1_default_t, option2_default_t>();
}
下面是一个如何使用流畅接口的示例:
struct my_option1_t { ... };
struct my_option2_t { ... };
int main()
{
make_foo<int>()
.option1(my_option1_t(...))
.option2(my_option2_t(...))
.run();
}
这当然是一个简化版本;在我的实际代码中有许多选项,而在该类的典型使用中,只指定了其中的几个选项,因此有理由使用fluent接口。
这个流畅接口的问题是它会导致不必要的模板实例化。例如,上面的示例将foo
实例化了三次:foo<int, option1_default_t, option2_default_t>
, foo<int, my_option1_t, option2_default_t>
,最后是foo<int, my_option1_t, my_option2_t>
,这是我想要的。
这是有问题的,因为foo
是一个大的类,并且实例化它是编译时昂贵的。
是否有一种方法可以改变流畅接口的实现,而不改变接口的使用方式,以便foo
只实例化一次,带最后的参数?
请注意,接口不改变的要求-即我作为使用流畅接口的例子给出的完全相同的代码,继续不变地工作-是这里的关键。如果没有这个要求,我可以很容易地重写流畅的接口,只实例化foo
一次(例如,我可以将接口更改为类似run_foo(make_foo<int>().option1(...).option2(...))
的东西)。
我不认为foo
有多个实例(正如MSalters在评论中指出的那样)。如果您想验证这一点,您可以为默认参数创建foo
的专门化,这将在实例化它们时导致错误。显然,这对于实际的生产版本来说不是很好,但这将证明没有多个实例化。
一旦您验证了foo
的多个实例化确实不是问题,问题就变成了:如何改进模板化代码的编译时间?一般来说,解决这个问题的方法是将代码分解为依赖较少模板参数(如果有的话)的帮助程序。这是一种痛苦,但可以有戏剧性的影响。避免在常用模板的所有翻译单元中实例化也可能是有益的。在c++ 2011中,您可以将外部模板与显式实例化结合使用。在c++ 2003中,你必须专门化你想要预实例化的代码。
- 未定义的类模板不会实例化以检查友元函数
- 如何编写模板重载函数,并在模板参数不允许实例化某个类时触发回退
- Clang和Gcc不同意实例化后的显式专业化
- 为什么不可能实例化原子对
- C++,不能实例化抽象类
- 为什么我不能实例化其构造函数在友元类中私有的类
- 编译的程序是否有可能不包含实例化的模板类
- 如何在不引起实例化的情况下获取功能模板的签名
- 在不指定实例化的情况下调用类模板的静态方法的方法
- 当丢失非const虚拟方法时,为什么我不能实例化类的const实例
- 访问不带实例化对象的非静态成员函数
- 为什么不总是实例化堆栈上的对象?C++
- 不能实例化抽象类的对象,但必须返回该类的对象
- 模板类的静态成员,除非显式专用化,否则不会实例化
- 如何避免在C++中使用右值引用的不必要实例
- C++私有构造函数(带参数)不允许实例化
- 在流畅接口中避免不必要的模板实例化
- 包装器不能实例化抽象类
- 转换基指针时出错:“不能实例化抽象类”
- 不能实例化抽象类