Может ли мой код использовать специализацию 'T' или 'const T &', в зависимости от того, что доступно? - PullRequest
4 голосов
/ 15 ноября 2010

Мы работаем с внутренней библиотекой, которая имеет класс StringBuilder, который используется для преобразования списка VariableValue объектов в строку.VariableValue объекты могут быть построены из произвольных типов (специализируя шаблонную функцию convertVariable).Вот код, который описывает сценарий:

struct VariableValue {
  // Construct a 'VariableValue' object, a variant type which can represent values of
  // one of four types: string, number (integer), boolean and floating point.
  explicit VariableValue( const std::string &serializedData );

  // Getters, typesafe; will yield an exception when calling the wrong getter.
  const std::string &asString() const;
  bool asBoolean() const;
  // ..  

  // Convert any VariableValue object into a string
  static std::string convertToString( const VariableValue &v );
};

// Template to be specialized so that user types can be casted into a
// VariableValue object
template <typename T>
VariableValue convertVariable( T v );

// Helper class to 'concatenate' multiple VariableValue objects into a single string.
class StringBuilder {
public:
  const std::string &result() const;

  template <class T>
  StringBuilder &operator<<( T v ) {
    return *this << convertVariable( v );
  }

private:
  std::ostringstream m_stream;
};

template <>
inline StringBuilder &StringBuilder::operator<<( const VariableValue &v ) {
  m_stream << VariableValue::convertToString( v );
  return *this;
}

Это все очень хорошо.Клиенты просто должны были предоставить соответствующую специализацию для шаблона convertVariable (наша библиотека уже предоставляет множество специализаций для различных типов), и тогда можно использовать StringBuilder.Почти.

Проблема в том, что он не работает с типами, которые нельзя копировать.Все шаблонные функции принимают свои аргументы по значению.А в случае шаблона convertVariable изменить подпись довольно дорого (поскольку существует довольно много специализаций).Поэтому, хотя я могу заставить шаблон StringBuilder::operator<< принимать const T &, это не сильно поможет, так как экземпляр convertVariable будет просто вызываться с T (так как часть ссылки на констант удаляется, покавывод типов шаблонов).Если я исправлю это, указав тип явно, как в:

class StringBuilder {
public:
  // ...

  template <class T>
  StringBuilder &operator<<( const T &v ) {
    return *this << convertVariable<const T &>( v );
  }
};

Компоновщик будет жаловаться, потому что он больше не находит старые специализации (например, template <> VariableValue convertVariable( int )), так как он ищет специализации, которые принимают ссылку-to-const.

Кто-нибудь знает, как я могу настроить класс StringBuilder, чтобы я мог передавать не копируемые объекты (т. е. объекты, тип которых не допускает ни конструирования, ни копирования) в operator<< функция?

Ответы [ 2 ]

3 голосов
/ 15 ноября 2010

Я не совсем уверен, что мой ответ поможет, но стоит попробовать.Из вашего поста я склонен думать, что подходящим решением является изменение подписи convertVariable.Вы говорите, что это дорого, потому что есть много специализаций, но я думаю, что это может быть на самом деле бесплатно в зависимости от того, как вы выбрали «специализацию».

Эта статья предлагает хорошийруководство для такого рода вещей:

Мораль # 1: если вы хотите настроить базовый шаблон функции и хотите, чтобы эта настройка участвовала в разрешении перегрузки (или всегда использовалась в случае точногосоответствовать), сделайте его простой старой функцией, а не специализацией.И, если вы предоставляете перегрузки, избегайте также предоставления специализаций.

[...]

Во-вторых, специализации шаблонов функций не перегружают .Это означает, что любые написанные вами специализации не будут влиять на то, какой шаблон будет использоваться, что противоречит ожиданиям большинства людей. В конце концов, если бы вы написали нешаблонную функцию с идентичной сигнатурой вместо специализации шаблона функции, то нешаблонная функция всегда выбиралась бы, потому что она всегда считается лучшим соответствием, чем шаблон .

Действительно, вместо того, чтобы специализироваться на типе UncopyableClass, вы вполне могли бы использовать перегрузку:

VariableValue convertVariable( const UncopyableClass &t ) { /* ... */ }

Это не специализация, а перегрузка, и она должна работать точно так, как ожидается.Однако обратите внимание, что StringBuilder::operator<< должен принимать константный опорный параметр.

0 голосов
/ 15 ноября 2010

Я не вижу никакого преимущества в использовании этого класса по сравнению с простым использованием интерфейса std::ostream.

Самый разумный совет, который у меня есть, это дамп класса и его ошибок (например, std::string const& str() const) и просто переход к классу потока, перегружая operator<< соответствующим образом для тех классов, которые требуют потоковой передачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...