tags:

views:

144

answers:

2

I'm having trouble figuring out how to solve a compiler error I'm running into. I've reduced it down to this simplest-case representation:

enum EAtomId { EAtomId_Test };

int StringFormat(char* o_dest, size_t i_destSizeChars, const char* i_format, ...);

template <size_t SIZE>
int StringFormat(char (&o_dest)[SIZE], EAtomId i_format, ...);

void func()
{
    char textBuffer[1000];
    StringFormat(textBuffer, EAtomId_Test, "hi there");
}

The compiler error is:

repro.cpp(17) : error C2666: 'StringFormat' : 2 overloads have similar conversions
    C:\Users\sbilas\Desktop\repro.cpp(9): could be 'int StringFormat(char *,size_t,const char *,...)'
    while trying to match the argument list '(char [1000], EAtomId, const char [9])'

That's the full error btw. I'm a little surprised it isn't listing both available versions..

I have a couple problems with this error. First, I don't see why it's ambiguous. Shouldn't the compiler see the char(&)[] version as the obvious case to match? And second, how can I prevent that enum from being converted to a size_t when it's doing the lookup? Seems like I'm running into some very specific C++ rules here.

The easiest workaround for me is to put the size_t for the buffer size in front of the actual buffer. But that will break all of our conventions where we put buffer then size in our code. Is there another way to do this?

This is on VC++ 2005 btw but it reproduces on a couple other compilers I have (this is in a cross-platform game).

+2  A: 

You have two declarations for StringFormat:

int StringFormat(char* o_dest, size_t i_destSizeChars,
                 const char* i_format, ...);

template <size_t SIZE>
int StringFormat(char (&o_dest)[SIZE], EAtomId i_format, ...);

Read up on argument dependent lookup and substitution rules when both a function template and an ordinary (non-template) function is available.

dirkgently
Neither of these points apply. ADL doesn't really apply here as it only adds to the lookup set, but both of these functions are visible to "normal lookup". The disambiguation of function template and non template function only apply where the two function types are identical. (13.3.3/1).
Richard Corden
@Richard Corden: I had intended overload resolution. These were not meant as points but rather for completeness' sake. I had ordered them incorrectly -- I should have put what I said last first and then added the points about ADL.
dirkgently
+2  A: 

The problem with the above code is due to ordinary overload resolution rules, and is not specific to templates. The following code with two ordinary functions still shows the ambiguity:

enum EAtomId { EAtomId_Test };
const int SIZE=1000;

int StringFormat(char * s
  , size_t i_destSizeChars
  , const char* i_format
  , ...);

int StringFormat(char (&a)[SIZE]
  , EAtomId i_format
  , ...);

void func()
{
    char textBuffer[SIZE];
    StringFormat (textBuffer, EAtomId_Test, "hi there");
}

The applicable rules for overload resolution work as follows (13.3.3):

  • For each set of arguments, work out the set of conversions that take place
  • The best function, is a function for which all conversions are as least as good as the conversions in all of the other overloads.

Taking each of these in turn we have:

Function 1:

  1. textBuffer->s: Array to pointer (Exact match 13.3.3.1.1/3) - Identity
  2. EAtomId_Test->i_destSizeChars: Possible promotion and Integral Conversion
  3. string literal->const char*: Array to pointer

Function 2:

  1. textBuffer->a: Identity Conversion
  2. EAtomId_Test->EAtomId: Identity Conversion
  3. string literal->ellipsis: Ellipsis Conversion

Comparing each of these conversions in turn you have:

  1. Function 1 is same as Function 2
  2. Function 2 better than Function 1
  3. Function 1 better than Function 2

So, neither function 1 nor function 2 is as least as good for all conversions and so the call is ambiguous.

Richard Corden
Thank you for your detailed response. If I understand you right, in param 2 "possible promotion and integral conversion" is just as strong as "identity conversion"? I would have expected the compiler would want to choose identity over a case where conversion would be necessary to make it work.Aside from reordering things, can you suggest a way around this problem? What I'm trying to do is get automatic length-safety for static sized string buffers. At the same time we have two ways to specify format strings - via literal and via "atom" (looked up from dictionary for localization purposes).
Scott Bilas