10

Compiling the following code

void f(char *, const char *, ...) {}
void f(const char *, ...) {}

int main()
{
    f("a", "b");
}

with clang gives me this error:

prog.cpp:6:2: error: call to 'f' is ambiguous
        f("a", "b");
        ^
prog.cpp:1:6: note: candidate function
void f(char *, const char *, ...) {}
     ^
prog.cpp:2:6: note: candidate function
void f(const char *, ...) {}
     ^

AFAIK string literals are constant in C++, and so the overload rules should drop the first variant from consideration, thus unambiguously resolving to the 2nd variant. But I guess that Clang makes them non-const for compatibility reasons (I know MSVC does that too).

What compiler flags to use to fix this? I'm already compiling with -std=c++11.

EDIT: Explicit cast to const char* solves this:

    f((const char*)"a", "b");

But if I'm correct on that the observed compiler behaviour isn't standard, I want to fix the compiler behaviour rather than the standard conforming code.

M.M
  • 138,810
  • 21
  • 208
  • 365
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • 1
    @Jarod42: because I like the name. If the code isn't broken then I don't think that "fixing" it is the right solution. – Yakov Galka Aug 15 '15 at 11:34
  • Define dummy first parameter e.g. `char` type. It will help C++ to differentiate original `sprintf` and your version. You will put 0 as first param. – i486 Aug 15 '15 at 11:36
  • 1
    I have searched some time ago for a compiler switch to deactivate this horrible conversion to `char*`, but couldn't find any. At least, not a documented one. Time to add one, I guess? MSVC has some "strict strings" switch btw. – dyp Aug 15 '15 at 11:37
  • 1
    Heh I just came up with the exact same MCVE character for character... pedantic minds think alike ;) – M.M Aug 15 '15 at 12:28
  • Actually, compiling with GCC 5.2.0 I get this very interesting diagnostic: _warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second: f("a", "b");_ (and no error). It appears that at least the GCC devs believe that the standard requires these calls to be ambiguous (and therefore presumably the clang++ error is actually conformant). – davmac Aug 15 '15 at 12:34
  • 1
    @davmac It would be ambiguous if the conversion to `char *` were permitted – M.M Aug 15 '15 at 12:36
  • 1
    @MattMcNabb I'm just copying what the compiler spit out, that's not _my_ interpretation, it's GCCs. – davmac Aug 15 '15 at 12:37
  • @davmac Yeah, I'm suggesting that the compiler is bugging out and allowing the invalid conversion, therefore leading to that diagnostic – M.M Aug 15 '15 at 12:38
  • @MattMcNabb but the wording of the diagnostic strongly suggests that the behaviour is as intended, no? – davmac Aug 15 '15 at 12:39
  • 1
    @davmac no, I have seen that diagnostic before in relation to conversion sequences involving `const` being added – M.M Aug 15 '15 at 12:39
  • @MattMcNabb I don't understand how that's relevant, although it may be that the text in the diagnostic is referring to the older ISO standard(s), in which your case your suggestion of a compiler bug seems correct (and in any case, I couldn't find anything in a C++'11 draft which would seem to allow the first function to be considered as a candidate). – davmac Aug 15 '15 at 12:55
  • @dav, just because the diagnostic strongly suggests that the behaviour is as intended does not mean that it actually is. As with _any other buggy error_. – Lightness Races in Orbit Aug 15 '15 at 13:01
  • @LightnessRacesinOrbit I'm sorry but you've completely missed the point (and I think you're confusing "intended" with "correct"). The message clearly shows that the compiler recognizes that seeing the first overload of `f` as a viable alternative for resolution would require a dubious conversions, but that it will accept it because the standard requires it to. I.e. the wording clearly implies that the standard mandates the behavior that actually occurs. If this behavior were _not_ intended, the devs certainly wouldn't bother to include a diagnostic explaining that it _was_ intended. – davmac Aug 15 '15 at 13:11
  • It's not like this issue is hard to fix; at least in clang, it's just two lines of code in `SemaOverload.cpp`. But I'm not sure if it's a good idea to that w/o introducing some kind of switch for those two modes. And btw, there does not seem to be any kind of such switch besides the `-fwritable-strings` which has a weird history. – dyp Aug 15 '15 at 15:07
  • @davmac: No, I perfectly understood your point, but I'm disagreeing with it. Nothing about this diagnostic implies that the build failure is intentional. It doesn't tell us either way. I have seen the message plenty of times in various situations — it's an odd one whose wording points out oddities in the actual standard requirements, not "intentions of GCC devs". However, in this situation, it's _wrong_: there is no conversion from `char const[N]` to `char*`. At best, in emitting that warning, GCC is buggy in the same way as clang. (It's just that it's being more permissive.) – Lightness Races in Orbit Aug 15 '15 at 15:59
  • @LightnessRacesinOrbit the warning is from GCC 5.2.0, for which there is no build failure, intentional or otherwise; so, I think you're confused somehow or at least are talking about a different issue than I am. I'm saying that the handling of overload resolution in GCC was done according to the standard, deliberately (and yeah, it may be the wrong version of said standard, but I already allowed this in comment above); I'm not sure why you'd disagree with that. – davmac Aug 15 '15 at 17:32
  • @davmac: I'd be happy to continue discussing this after you've stopped trying to use arrogant accusations of ignorance and confusion as a way to make your points. Let me know when you're ready to go. – Lightness Races in Orbit Aug 15 '15 at 17:33
  • @LightnessRacesinOrbit no arrogant accusations of ignorance were intended. It seems clear to me that you misunderstood the point I was trying to make. I have re-read my comments to be sure and I don't feel that they insult you in any way. I certainly did not accuse you of ignorance. – davmac Aug 15 '15 at 17:37
  • @davmac: _"you've completely missed the point"_ _"you're confusing 'intended' with 'correct'"_ _"you're confused somehow"_ Cut it out. Keep it about the topic, not about the person who disagrees with you. After all, who knows — perhaps it's you who's confused. You don't seem to acknowledge that possibility in your remarks. – Lightness Races in Orbit Aug 15 '15 at 17:38
  • @LightnessRacesinOrbit it doesn't seem like we're going to have a productive discussion, so let's just leave it. I didn't intend to insult you. – davmac Aug 15 '15 at 17:39
  • @davmac: Right okay if you'd rather simply end the discussion than be reasonable. Sigh. – Lightness Races in Orbit Aug 15 '15 at 17:39

1 Answers1

5

I think this is a bug. Conversion of string literals to char * was removed in C++11 and I am not aware of any provision in overload resolution for a conversion sequence involving it.

As a workaround that does not involve changing every single call to f, you can write another overload that explicitly catches every call with a string literal, by capturing the array by reference:

template<size_t N, typename ...F>
void f(char const (&a)[N], F&&... args)
{
    f((char const *)a, std::forward<F>(args)...);
}
M.M
  • 138,810
  • 21
  • 208
  • 365