3

I am attempting to program a string concatenation function which utilizes my 3D library's string conversion functions, implemented with a variadic template.

The library's conversion function behaves unusually if a string (either const char[] literal or std::string) is passed to it. as it does not actually possess functions for those types, so I want to specialize the template to pull those out and not run them through the converter. Optimization would be a good reason even if the converter handled them.

template<typename T>
inline String c(T a)
{
    return Ogre::StringConverter::toString( a );
}
template<>
inline String c(String s)
{
    return s;
}
template<>
inline String c(const char s[])
{
    return s;
}

template<typename T, typename... Args>
inline String c(T a, Args... args)
{
    return Ogre::StringConverter::toString( a ) + c(args...);
}
template<typename... Args>
inline String c(String s, Args... args)
{
    return s + c(args...);
}
template<typename... Args>
inline String c( const char s[], Args... args)
{
    return s + c(args...);
}

However, when I compile my program, string literals sometimes fall through the const char[] specialization and are handled by the base, unspecialized template. The command:

U::c( "This is dMap[500][500]: ", dMap[500][500], " and this is 5: ", 5, "." )

returns

This is dMap[500][500]: 112true5.

"true" being what toString returns if a string literal is passed to it. Debugging confirms that the second string literal is caught by the generic String c(T a, Args... args), but not the first or third, which are handled by the specialization.

This seems related to the problem mentioned in Selecting string literal type for template specialization, but changing my template parameter declaration match those suggested in that solution, inline String c( const char (&s) [N], Args... args ), cause the first parameter to be caught by the specialized template, but not the second or third. Something unusual is happening here and I cannot figure out what it is.

Community
  • 1
  • 1
  • Name lookup problems. In particular, when the first (general) variadic version is defined, the later two overloads are not in scope, and unqualified lookup for `c` will not consider them. Declare them all, and then define them. – T.C. May 13 '15 at 22:48
  • Incidentally, the single-argument version for `String` and `const char *` should not be specializations. – T.C. May 13 '15 at 22:54

1 Answers1

4

In

template<typename T, typename... Args>
inline String c(T a, Args... args)
{
    return Ogre::StringConverter::toString( a ) + c(args...);
}

unqualified name lookup for c in c(args...) is performed in the template definition context, which means that it only finds overloads of c declared up to this point, and will not find your later c overloads. (ADL is performed using both the definition and the instantiation contexts, but in your case it looks like there's no ADL.)

Declare them all first:

template<typename T, typename... Args>
inline String c(T a, Args... args);
template<typename... Args>
inline String c(String s, Args... args);
template<typename... Args>
inline String c( const char s[], Args... args);

before you define them, so that all three overloads can be found.


Incidentally, you should not use specializations for the single-argument case. Delete the template<> and use overloads instead. As written right now, U::c(""); will not behave the way you want it to.

Demo.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • That fixed the issue. You're right about the single argument ones as well, I think I was stuck in the template mindset and failed to realize those weren't quite right. Thanks a lot, it makes sense and works properly now. –  May 13 '15 at 23:10