8

Following program compiles fine and works as expected. Its output is:

1
2

#include <stdio.h>

class Foo
{
public:
  void Bar(const char* b, ...) { printf("1\n"); };
  void Bar(int a, const char* b, ...) { printf("2\n"); };
};

int main()
{
  Foo foo1;
  foo1.Bar("Test", "xx", 1, 2);
  foo1.Bar(1, "xx", "xx", 2, 2);
}

Now if I change the int parameter of the second Bar function into bool and foo1.Bar(1, "xx", "xx", 2, 2); into foo1.Bar(true, "xx", "xx", 2, 2);, then the following line won't compile and I get the error: 'Foo::Bar': 2 overloads have similar conversions:

  foo1.Bar("Test", "xx", 1, 2);

The whole program that doesn't compile:

#include <stdio.h>

class Foo
{
public:
  void Bar(const char* b, ...) { printf("1\n"); };
  void Bar(bool a, const char* b, ...) { printf("2\n"); };
};

int main()
{
  Foo foo1;
  foo1.Bar("Test", "xx", 1, 2);  // error: 'Foo::Bar': 2 overloads have similar conversions
  foo1.Bar(true, "xx", "xx", 2, 2);
}

I don't understand why there is an ambiguity in the second case.

EDIT

But if pointers implicitly convert to bool, why does following compile ?

#include <stdio.h>

class Foo
{
public:
  void Bar(const char* b) { printf("1\n"); };
  void Bar(bool a) { printf("2\n"); };
};

int main()
{
  Foo foo1;
  foo1.Bar("Test");
  foo1.Bar(true);
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • Similar question http://stackoverflow.com/questions/26413951/overloaded-bool-string-ambiguity – robor Oct 04 '16 at 15:26
  • This has nothing to do with variadic functions whatsoever, which you'd have discovered had you constructed a _minimal_ testcase. – Lightness Races in Orbit Oct 04 '16 at 15:29
  • 1
    @LightnessRacesinOrbit I just edited the question and added a minimal test case without variadic functions that compiles... – Jabberwocky Oct 04 '16 at 15:36
  • @robor78 the question is similar but not the same. Here we have `const char*` which is a _builtin type_, whereas in the other question there is a `string` which is a _user defined type_. – Jabberwocky Oct 04 '16 at 15:45
  • (Add headers and namespaces please. You should know by now, at 11.9k rep, how to form a [MCVE].) – Lightness Races in Orbit Oct 04 '16 at 15:51
  • @LightnessRacesinOrbit sorry I forgot `#include `. Apart from that it _was_ minimal and verifiable. And no need for namespaces here (at least with my environnment, Microsoft VS2015). – Jabberwocky Oct 04 '16 at 15:55
  • Nitpicking now but prefer to code to standards rather than relying on implementation specifics, so `std::printf` or a suitable `using namespace` declaration would be appropriate! Anyway, as to the answer to your question, meh dunno. Would have to delve deeper into function overload selection mechanics than I feel like right now ;) – Lightness Races in Orbit Oct 04 '16 at 16:12

2 Answers2

5

This is fine:

#include <stdio.h>

class Foo
{
public:
  void Bar(const char* b, ...) { printf("1\n"); };
  void Bar(bool a, ...) { printf("2\n"); };
};

int main()
{
  Foo foo;
  foo.Bar("Test");
}

This is not fine:

#include <stdio.h>

class Foo
{
public:
  void Bar(const char* b, ...) { printf("1\n"); };
  void Bar(bool a, char another, ...) { printf("2\n"); };
};

int main()
{
  Foo foo;
  foo.Bar("Test", 'c');  // Ambiguous!
}

In the first case, the first version of Bar() is clearly better. But in the second case, it is not so clear anymore because, while the first version has a better match for parameter 1, the second version has a better match for parameter 2.

The remaining question is: why does using 'int' instead of 'bool' avoid the ambiguity? The answer is that a pointer may implicitly convert to bool but not int.

Donghui Zhang
  • 1,133
  • 6
  • 8
  • I edited the question: so why is the last case fine ? – Jabberwocky Oct 04 '16 at 15:58
  • Your newly added case is equivalent to the first case in my answer: if you call with 'const char*', the version of Bar() that takes 'const char*' is clearly better; and if you call with 'bool', the version of Bar() that takes 'bool' is clearly better. An implicit conversion is still a conversion. A function that does not need to convert its argument, if exists, takes precedence. – Donghui Zhang Oct 04 '16 at 16:05
5

When you match "Test", "xx", 1, 2 against const char*, ..., the conversion sequence for the first argument has exact match rank, the second through fourth one are ellipsis conversion sequences. So (exact match, ellipsis, ellipsis, ellipsis).

When you match "Test", "xx", 1, 2 against bool, const char*, ..., the first conversion sequence for the first argument has conversion rank; the second is an exact match, the third and fourth are ellipsis conversion sequences. In other words, (conversion, exact match, ellipsis, ellipsis).

Exact match beats conversion; everything beats ellipsis (see [over.ics.rank]). Thus we have a so-called criss-cross situation here where one function has a better conversion sequence for one argument and the other function has a better conversion sequence for another argument. Since a necessary (but not sufficient) condition for a function to be better than another function is that none of the conversion sequences are worse than those of the other function ([over.match.best]/1), neither of those two functions is better than the other.

T.C.
  • 133,968
  • 17
  • 288
  • 421