Question

I have a template function that's in charge of writing a template value to a stream. It looks like this :

template < typename T >
void Write( T value, std::ostream& stream, endianness_t endian );

I have implemented the versions for basic types : int, uint, float, etc. Now, if I want to write a more complex structure, say, a std::string, that I declared like this :

template<>
inline void Write( const std::string& value, std::ostream& stream, endianness_t endian ) { // Notice the reference
...
}

I can't call it without calling explicitly calling the "pass-by-reference" version :

Write( strValue, stream, LITTLE_ENDIAN ); // error : tries to call Write<std::string>, undefined
Write< const std::string& >( strValue, stream, LITTLE_ENDIAN ); // OK, Write<const std::string&> is properly defined

The problem being that, it's too verbose for what I want to do.

My question then : how can I make the compiler guess that the version I want used is the "pass-by-reference" one ?

Do I have to change my template function to take const references ? If so, can I specialize if to use "pass-by-copy" for primitive types ?

Was it helpful?

Solution

You should prefer overloads to template specialization, and doing this solves your problem I think:

inline void Write( const std::string& value, std::ostream& stream, endianness_t endian ) {
    // ...
}

I also recommend going back and changing all your specializations that you mentioned for int and stuff to overloads instead.

OTHER TIPS

Do not provide overloads for every possible type you want to use. This breaks the logic of using generic code in the first place. That been said, the need to provide a specialization for every type maybe points out that this was not the most successfull application of templates, not to mention that messing around with function template specialization is not the prettiest choice as Sutter says

Since you need a logic separation between mutually exclusive types (built ins and non built ins) here's an alternative :

template<class T>
typename std::enable_if< std::is_fundamental<T>::value >::type foo_func(T arg)
{
    std::cout << "Call for built in types" << std::endl;
}
template<class T>
typename std::enable_if< !std::is_fundamental<T>::value >::type foo_func(T  const &arg)
{
    std::cout << "Call for non built in types" << std::endl;
}

To explain the code here what happens:

1) std::is_fundamental will return false or true at compile time depending on whether T is user defined or a built in type respectively.

2) std::enable_if will "allow" the declaration of the function template for true in the first case and false in the second, so no resolution problems can happen.

3) so when calling foo_func for built in types, the first version will be used and T (int, float and so on) is passed by value (which is faster for built ins) and ... well you can imaging what happens in the other case.

You could change the generic template to pass by const ref:

template < typename T >
void Write( const T& value, std::ostream& stream, endianness_t endian );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top