【问题标题】:Avoiding unnecessary template instantiations in a fluent interface在流畅的界面中避免不必要的模板实例化
【发布时间】:2012-01-02 00:51:25
【问题描述】:

我有一个带有一些可选模板参数的类:

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();
};

以下fluent interface 用于指定它们:

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();
}

这当然是简化版;在我的真实代码中,有很多选项,而在这个类的典型使用中,只指定了其中的几个,因此是流畅接口的理由。

这个流畅界面的问题在于它会导致不必要的模板实例化。例如,上面的示例将foo 实例化了三次:foo&lt;int, option1_default_t, option2_default_t&gt;foo&lt;int, my_option1_t, option2_default_t&gt;,最后是foo&lt;int, my_option1_t, my_option2_t&gt;,这就是我想要的。

这是有问题的,因为foo 是一个大类并且实例化它在编译时开销很大。

有没有一种方法可以改变流畅接口的实现而不改变接口的使用方式,这样foo 只用最终参数实例化一次?

请注意,接口不变的要求——即我作为使用流式接口的示例给出的完全相同的代码,继续保持不变——是这里的关键。如果没有这个要求,我可以轻松地重写流畅的接口,只实例化一次foo(例如,我可以将接口更改为run_foo(make_foo&lt;int&gt;().option1(...).option2(...)) 之类的东西)。

【问题讨论】:

  • 你确定这个模板实例化是瓶颈吗?
  • 你确定有 3 个 foo 实例化了吗?我不明白他们为什么会这样。我只看到来自foo_runner&lt;int, my_option1_t, my_option2_t&gt;::run() 的实例化。

标签: c++ performance templates fluent-interface


【解决方案1】:

我不认为foo 有多个实例化(正如 MSalters 在评论中指出的那样)。如果要验证这一点,可以为默认参数创建 foo 的特化,这会在实例化时导致错误。显然,这对实际的生产版本不利,但这将证明没有多个实例化。

一旦您确认foo 的多个实例化确实不是问题,问题就变成了:如何提高模板代码的编译时间?通常,这个问题的答案是将代码分解为依赖于较少模板参数(如果有的话)的帮助程序。这有点痛苦,但会产生巨大的影响。避免在常用模板的所有翻译单元中实例化也可能是有益的。使用 C++2011,您可以使用结合显式实例化的外部模板。使用 C++2003,您必须专门化您想要预实例化的代码。

【讨论】:

  • 你说得对,上面的代码中没有foo 的多个实例化......在减少我的实际代码的过程中出了点问题(确实有foo 的多个实例化,从我从foo) 的两个不同实例中收到一条错误消息这一事实就可以看出这一点。我会调查的。
猜你喜欢
  • 1970-01-01
  • 2014-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多