C++ 中的自动评估策略选择

发布于 2024-09-08 03:23:03 字数 2020 浏览 11 评论 0原文

考虑以下函数模板:

template<typename T> void Foo(T)
{
  // ...
}

如果 T 恰好是整型,或者至少是复制成本低的类型,那么按值传递语义就有意义。 另一方面,如果 T 恰好是复制成本高昂的类型,则使用传递[const]引用语义更有意义。

让我们假设您正在编写一个库。理想情况下,作为库实现者,您的工作是为消费者提供尽可能通用且高效的干净 API。那么,如何提供一个通用接口来满足这两种类型的参数传递策略呢?


这是我第一次尝试让它工作:

#include <boost/type_traits.hpp>

template<typename T> struct DefaultCondition
{
  enum {value = boost::is_integral<T>::value /* && <other trait(s)> */};
};

template< typename T, class Condition = DefaultCondition<T> > class Select
{
  template<bool PassByValue = Condition::value, class Dummy = void> struct Resolve
  {
    typedef T type;
  };

  template<class Dummy> struct Resolve<false, Dummy>
  {
    typedef const T& type;
  };

  public: typedef typename Resolve<>::type type;
};

典型用法:

template<typename T> class EnterpriseyObject
{
  typedef typename Select<T>::type type;

  public: explicit EnterpriseyObject(type)
  {
    // ...
  }
};

struct CustomType {};

void Usage()
{
  EnterpriseyObject<int>(0); // Pass-by-value.
  (EnterpriseyObject<CustomType>(CustomType())); // Pass-by-const-reference.
}

当然,这间接破坏了非类模板的隐式模板参数推导:

template<typename T> void Foo(typename Select<T>::type)
{
  // ...
}

void Usage()
{
  Foo(0);      // Incomplete.
  Foo<int>(0); // Fine.
}

这可以使用 Boost.Typeof 库“修复”和一个宏,如WinAPI

#define Foo(Arg) ::Foo<BOOST_TYPEOF((Arg))>((Arg))

尽管这只是一个准可移植的黑客。

正如您所看到的,我的一般方法并不是对所有情况都令人满意。


作为一名业余程序员,我既没有实际经验,也无法获得生产质量的代码作为参考。我还意识到,这似乎是过早优化的糟糕情况,但我真正对以下几件事感兴趣:

  1. 您过去是否使用过这种类型的优化*?
  2. Boost(或任何其他公共)库是否已经提供类似的功能?
  3. 如果#1 或#2 的答案是“是”——如何处理非类模板情况?
  4. 对于这样的事情,是否存在我没​​有看到的明显陷阱?
  5. 最后,这是否是明智之举?

* 未分析。 ;)

Consider the following function template:

template<typename T> void Foo(T)
{
  // ...
}

Pass-by-value semantics make sense if T happens to be an integral type, or at least a type that's cheap to copy.
Using pass-by-[const]-reference semantics, on the other hand, makes more sense if T happens to be an expensive type to copy.

Let's assume for a second that you are writing a library. Ideally, as a library implementer, your job is to provide your consumers with a clean API that is both as generic and efficient as possible. How then, do you provide a generic interface that caters to both types of argument passing strategies?


Here is my first attempt at getting this to work:

#include <boost/type_traits.hpp>

template<typename T> struct DefaultCondition
{
  enum {value = boost::is_integral<T>::value /* && <other trait(s)> */};
};

template< typename T, class Condition = DefaultCondition<T> > class Select
{
  template<bool PassByValue = Condition::value, class Dummy = void> struct Resolve
  {
    typedef T type;
  };

  template<class Dummy> struct Resolve<false, Dummy>
  {
    typedef const T& type;
  };

  public: typedef typename Resolve<>::type type;
};

Typical usage:

template<typename T> class EnterpriseyObject
{
  typedef typename Select<T>::type type;

  public: explicit EnterpriseyObject(type)
  {
    // ...
  }
};

struct CustomType {};

void Usage()
{
  EnterpriseyObject<int>(0); // Pass-by-value.
  (EnterpriseyObject<CustomType>(CustomType())); // Pass-by-const-reference.
}

This, of course, indirectly breaks implicit template argument deduction for non-class templates:

template<typename T> void Foo(typename Select<T>::type)
{
  // ...
}

void Usage()
{
  Foo(0);      // Incomplete.
  Foo<int>(0); // Fine.
}

This can be "fixed" with the Boost.Typeof library and a macro, a la the WinAPI:

#define Foo(Arg) ::Foo<BOOST_TYPEOF((Arg))>((Arg))

Though this is just a quasi-portable hack.

As you can see, my general approach is not really satisfactory for all cases.


As a hobbyist programmer, I neither have real-world experience nor do I have access to production-quality code for reference. I also realize that this might seem like a bad case of premature optimization, but I'm genuinely interested in a couple of things:

  1. Do you, or have you used this type of optimization* in the past?
  2. Does the Boost (or any other public) library already provide similar functionality?
  3. If the answer to #1 or #2 is a 'yes' -- how is the non-class template case handled?
  4. Are there any obvious pitfalls that I'm not seeing with something like this?
  5. Finally, is this even a sane thing to do?

* Not profiled. ;)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

遇到 2024-09-15 03:23:03
  1. 是的。一直以来。我自己用的。
  2. 是的,使用Boost.Utility 的调用特征 :)

    用法是...

    模板 <类型名称 T>
    void foo(boost::call_traits::param_type 参数)
    {
        // 使用参数
    }
    
  3. 据我所知,非类模板是按值传递的,除非它更快。由于部分模板专门化,它可以相对容易地定制。

  4. 抱歉,没有真正阅读您所做的事情,它看起来就像我几个月前经历的那样。因此,无法真正回答这个问题。我的建议是通读 Boost.Utility。

  5. 当然!

  1. Yes. All the time. I use it myself.
  2. Yes, use Boost.Utility's Call Traits :)

    Usage would be...

    template <typename T>
    void foo(boost::call_traits<T>::param_type param)
    {
        // Use param
    }
    
  3. As far as I know, non-class templates are passed-by-value unless it is faster to not. Thanks to partial template specialization, it can be customized relatively easily.

  4. Sorry, didn't really read what you did, it just looked like exactly what I went through a few months ago. Therefore, can't really answer this one. My recommendation is just to read through Boost.Utility.

  5. Of course!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文