Can my code use the specialization "T" or "const T &", depending on what is available?

We are working with an internal library that has the StringBuilder class, which is used to convert a list of VariableValue objects to a string. VariableValue objects can be constructed from arbitrary types (by specializing the convertVariable template convertVariable ). Here is the code describing the scenario:

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

All this was very good. Clients simply had to provide the appropriate specialization for the convertVariable template (our library already provides many specialties for different types), and then StringBuilder can be used. Nearly.

The problem is that it does not work with types that cannot be copied. All template functions take their argument by value. And in the case of the convertVariable template, convertVariable quite difficult to change the signature (because there are quite a few specializations). Therefore, even if I can make the StringBuilder::operator<< template occupy const T & , this will not help, since the convertVariable instance will only be called with T (since the reference-to-const part is deleted during template type output). If I fix this by specifying the type explicitly, as in:

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

The component will complain because it no longer finds old specializations (for example, template <> VariableValue convertVariable( int ) ), since it is looking for specializations that take a const-reference.

Does anyone know how I can customize the StringBuilder class StringBuilder that I can pass non-copyable objects (that is, objects whose type does not allow you to create copies or assign copies) to the operator<< function?

+4
source share
2 answers

I'm not quite sure my answer will be useful, but worth a try. From your post, I am inclined to think that a suitable solution is to change the signature of convertVariable . You say that it is expensive because there is a lot of specialization, but I think that it can really be free depending on how you decide to "specialize".

This article offers a good guide for things like this:

Moral # 1: If you want to customize the function base template and want the configuration to participate in (or to always be used in case of exact correspondence), make is a simple old function, not a specialization. And if you provide overloads, avoid specializations.

[...]

On the other hand, the specialization function template does not overload . This means that any specializations you write will not affect which template is used, which contradicts most people intuitively expect. After all, if you wrote nontemplate with an identical signature instead of a specialized function, the nontemplate function would always be chosen because it is always considered a better match than the template .

Indeed, instead of specializing for the UncopyableClass type UncopyableClass you can well use overloading:

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

This is not a specialization, but an overload, and it should work exactly as expected. However, note that StringBuilder::operator<< must accept the link parameter const.

+3
source

I see no advantage in using this class or just using the std::ostream .

The most convincing advice would be to dump the class and its errors (for example, std::string const& str() const ) and simply continue the stream class, overloading operator<< accordingly for those classes that need streaming.

0
source

All Articles