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.
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)...);
}
Related
I'm encountering an unexpected issue in some code I'm writing now and I'm not sure which compiler is correct.
We have a multi-argument constructor which takes const char*, const char*, but it is declared explicit:
constexpr explicit Wrapper(const char* a, const char* b) : pair(a,b){}
And then we have a function which takes Wrapper and an overload which takes a std::pair<const char*, const char*>
void q(Wrapper w);
void q(std::pair<const char *, const char *> w);
And then we have code like this, which I would expect to call the second overload:
q({"a", "b"});
This compiles fine on clang, but fails to compile on both GCC and MSVC. I've been trying to look for any mention of explicit multi-arg constructor in the standard and if there's anything mentioning this ambiguity but I haven't found the relevant text. I'm just wondering which behavior is correct and which is wrong?
godbolt link: https://godbolt.org/g/2aYUov
Using your provided constructor for Wrapper, g++ 7.1.1 gives me the following error:
main.cpp: In function ‘int main()’:
main.cpp:29:25: error: converting to ‘Wrapper’ from initializer list would use explicit constructor ‘constexpr Wrapper::Wrapper(const char*, const char*)’
Wrapper w({"a", "b"});
^
So it seems that the explicit keyword on the Wrapper constructor is well taken into account when manually triggering a conversion.
However, the error on the call to q seems to indicate that overload resolution is ignoring the explicit keyword:
main.cpp:34:17: error: call of overloaded ‘q(<brace-enclosed initializer list>)’ is ambiguous
q({"a", "b"});
^
main.cpp:16:6: note: candidate: void q(Wrapper)
void q(Wrapper w)
^
main.cpp:21:6: note: candidate: void q(std::pair<const char*, const char*>)
void q(std::pair<const char *, const char *> w)
^
This could be a bug in g++, which would need to be verified with other sources.
#include <iostream>
using namespace std;
void f(const char* arg)
{
cout << "arg is a pointer" << endl;
}
template<size_t N>
void f(const char (&arg)[N])
{
cout << "arg is an array." << endl;
}
int main()
{
f("");
}
My compiler is clang 3.8.
The output is:
arg is a pointer
However, according to cppreference.com,
The type of an unprefixed string literal is const char[].
Why does the overload resolution not behave as expected?
It does behave as expected, you just need to adjust your expectations ;-)
const char[1] and const char (&)[1] are different types.
The conversions to const char* (array-to-pointer conversion) and const (&char)[1] (identity conversion) are both considered exact matches, but a non-template is a better match than a template.
If you write a non-template size-specific overload,
void f(const char (&arg)[1])
you will get an error that the function call is ambiguous.
#molbdnilo's answer is correct. To add one detail: Your intuition would be correct and the compiler would prefer to avoid the array-to-pointer conversion by calling the template. But lvalue transformations (lvalue-to-rvalue, array-to-pointer, and function-to-pointer) are specifically ignored in overload ranking, according to [over.ics.rank] §13.3.3.2/3.2.1.
There is a workaround: add a fake volatile to restore the balance of overload preference. Just be sure to remove it by const_cast before using the parameter.
class myClass {
int arr[100];
public:
void *get(long i, void* const to) const;
void *get(long i, bool nog);
void *tstfn(void* const to) { return get(0L,to); }
};
gcc -Wall says:
dt.cpp: In member function ‘void* myClass::tstfn(void*)’:
dt.cpp:6:49: 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: [enabled by default]
dt.cpp:4:9: note: candidate 1: void* myClass::get(long int, void*) const
dt.cpp:5:9: note: candidate 2: void* myClass::get(long int, bool)
Both function calls require a type conversion:
calling the void* function requires adding a const qualifer to this
calling the bool function requires converting to from void* to bool.
So, by the overload resolution rules, neither is a "better" match than the other, and the call is considered ambiguous.
Perhaps you can add const to the second function; perhaps you could remove it from the first (although I'd prefer not to); perhaps you can do an explicit type conversion of either this or to to force your preferred override.
Because void *get(long i, void* const to) is const.
This means that calling it from tstfn (which is non-const) would require qualification conversion for this from myClass* to const myClass*, so calling both functions would require a conversion for the arguments (this is treated in the same way as other arguments), so the call is ambiguous.
Simply because your testfn is a non-const function, which would call the non-const version of get. The non-const function get, takes bool not const void*. Had only one get function was there (possibly taking void* as the second argument, irrespective of its constness), then would be called without ambiguity.
I got following unexpected overload resolution behavior with the visual studio compiler (tested in VS2010 and VS2012).
Minimal example:
#include <iostream>
#include <string>
void f(void *)
{
std::cout << "f(void*)\n";
}
void f(const std::string &)
{
std::cout << "f(const std::string &)\n";
}
int main()
{
f("Hello World!");
}
Output:
> f(void *)
Expected Ouptut:
> f(const std::string &)
Compiling with GCC(tested with 4.6.3) generates the expected output.
If I comment out the "const std::string &" version of f(), visual studio happily compiles on /W4 without any warnings, while GCC emits following error (as expected): "invalid conversion from 'const void*' to 'void*' [-fpermissive]".
Does anyone know why visual studio behaves in that way, choosing basically a const cast overload over a conversion to std::string for char[]?
Is there any way to prohibit this behavior, or at least get VS to generate a warning?
For VS 2013 Microsoft documents silently dropping const for string literals as a Microsoft-specific behavior for C++:
Microsoft Specific
In Visual C++ you can use a string literal to initialize a pointer to
non-const char or wchar_t. This is allowed in C code, but is
deprecated in C++98 and removed in C++11.
...
You can cause the compiler to emit an error when a string literal is converted to a non_const character when you set the /Zc:strictStrings (Disable string literal type conversion) compiler option.
For versions earlier than VS 2013 (for example VS 2012's documentation), Microsoft documents string literals in C++ as using the C convention of being non-const array of char.
I don't see why it is unexpected. The conversion of char
const[] to a std::string involves a user defined conversion;
the conversion to void* doesn't. And a conversion involving
a user defined conversion is always "less good" than one which
doesn't involve a user defined conversion.
The real issue here is that C++ doesn't have a built-in string
type, and the string literals don't have type std::string.
The normal solution is to provide an overload for char const*
as well:
void f( void* );
void f( std::string const& );
inline void f( char const* p ) { f( std::string( p ) ); }
This additional overload will pick up string literals.
(As a general rule: anytime you're overloading: if one of the
overloads is for std::string, provide one for char const* as
well, if any are for arithmetic types, provide one for
int, to catch integral literals, and if any are for a floating
point type, provide one for double, to catch floating point
literals.)
The apparent issue is, as noted by others, that MSVC allows implicit conversion from string literals to non-const char*, and thence to void*.
I say apparent because your void* overload should be a void const* overload, as it does not change the pointed to data. Doing so will make things 'worse', as calling it with a string literal will now unambiguously select the void const* overload. However this illustrates what is going wrong: "" is a char const(&)[1] (an array of const char), not a std::string, and char const(&)[1] is closer related to pointers than to std::string. Relying on the overload picking std::string over a pointer is fragile, even on gcc, as making your code const correct breaks it!
To fix this we can write a greedy overload for std::string.
template<typename S, typename=typename std::enable_if<std::is_convertible<S,std::string>::value>::type>
void f(S&&s){
f(std::string{std::forward<S>(s)});
}
with the above two overloads left intact (except const added).
Or (better) via tag dispatching:
void f(void const* v, std::false_type){
std::cout << "f(void*)\n";
}
void f(std::string const& s, std::true_type){
std::cout << "f(const std::string &)\n";
}
template<typename T>
void f(T&&t){
return f(std::forward<T>(t), std::is_convertible<T,std::string>() );
}
both of which are ways to do manual function overload dispatching, biased towards std::string.
live example
Note that std::string literals are now possible in C++, but I would advise against requiring them on the basis of fragility.
I don't understand why this program produces the output below.
void blah(const char* ) {printf("const char*\n");}
void blah(const std::string&) {printf("const string ref\n");}
template<class t>
void blah(t) {printf ("unknown\n");}
int main(int, char*)
{
blah("hi");
char a[4];
blah(a);
std::string s;
blah(s);
getch();
}
Outputs:
const char*
unknown
const string
In VS2008. It is willing to convert the std::string to a const reference, but why won't it convert the char* to a const char* and use the overload?
The type of "hi" is const char[3], whereas the type of a is char[4].
So, the first call requires only array-to-pointer conversion (aka "decay"). The third call requires only binding an object to a reference-to-const (I don't think "converting" is the correct terminology for reference-binding, although I may be mistaken). The second call would require array decay and a pointer conversion in order to call the const char* overload.
I claim without actually checking the overload resolution text in the standard that this extra step is what makes the template a better match than the const char* overload.
Btw, if you change "unknown\n" to "%s\n", typeid(t).name() then you can see what the type t was deduced as. For your code, it is deduced as char* (because arrays can't be passed by value), but see what happens if you change the template to take a t& parameter instead of t. Then t can be deduced as char[4].