Автоматический выбор стратегии оценки в C ++ - PullRequest
2 голосов
/ 07 июля 2010

Рассмотрим следующий шаблон функции:

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

Семантика передачи по значению имеет смысл, если T является целочисленным типом или, по крайней мере, типом, который дешево копировать. Использование семантики передачи по [const] ссылкам, с другой стороны, имеет больше смысла, если T оказывается дорогостоящим типом для копирования.

Давайте на секунду предположим, что вы пишете библиотеку. В идеале, как разработчик библиотеки, вы должны предоставить своим клиентам чистый 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. Наконец, это даже нормальное занятие?

* Не профилировано. ;)

1 Ответ

2 голосов
/ 07 июля 2010
  1. Да. Все время. Я использую это сам.
  2. Да, используйте Характеристики вызовов Boost.Utility :)

    Использование будет ...

    template <typename T>
    void foo(boost::call_traits<T>::param_type param)
    {
        // Use param
    }
    
  3. Насколько я знаю, не-классовые шаблоны передаются по значению, если это не происходит быстрее. Благодаря частичной специализации шаблона его можно относительно легко настроить.

  4. Извините, на самом деле не читал, что вы делали, просто выглядело именно так, как я прошел несколько месяцев назад. Поэтому не могу ответить на этот вопрос. Я рекомендую просто прочитать Boost.Utility.

  5. Конечно!

...