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.
Related
Consider the following code:
struct A {
int& getAttribute();
const int& getAttribute() const;
};
std::vector<int> foo(const std::vector<A>& as) {
std::vector<int> ints;
std::transform(as.begin(), as.end(), std::back_inserter(ints),
std::mem_fn(&A::getAttribute));
return ints;
}
Its compilation (g++ -std=c++14 -c mem_fn.cpp, g++ version 7.5.0) fails with the following error:
error: no matching function for call to
‘mem_fn(<unresolved overloaded function type>)’
However,
If we keep only the const int& getAttribute() const method, the compilation succeeds
If we keep only the int& getAttribute() method, the compilation fails with the following error messages:
/usr/include/c++/7/bits/stl_algo.h:4306:24:
error: no match for call to
‘(std::_Mem_fn<int& (A::*)()>) (const A&)’
/usr/include/c++/7/functional:174:27:
error: no matching function for call to
‘__invoke(int& (A::* const&)(), const A&)’
/usr/include/c++/7/bits/invoke.h:89:5:
error: no type named ‘type’ in
‘struct std::__invoke_result<int& (A::* const&)(), const A&>’
Alternatively, we could use a lambda here or help the compiler by explicitly specifying the type of a matching method as mem_fn's template argument:
std::mem_fn<const int& () const>(&A::getAttribute).
So, it looks like, as the int& getAttribute() method does not fit, the const int& getAttribute() const method is the one that should have been choosen by the by the compiler in the original code.
Why does compiler fail to select it and report the <unresolved overloaded function type> error?
The compiler should choose pointer of the function at the moment of mem_fn creation. It can only look at the std::mem_fn(&A::getAttribute) expression, but cannot look up how it's used. It's possible to work around with user-defined conversion operators, but is generally not done.
So, your reasoning about future uses of this mem_fn does not apply.
You have to specify the exact overload of &A::getAttribute to use. static_cast to a fixed type will work (it's a special case where unresolved overloaded functions are permitted):
std::transform(
as.begin(), as.end(), std::back_inserter(ints),
std::mem_fn(static_cast<const int &(A::*)() const>(&A::getAttribute)));
In Visual C++ 2017 (with /std:c++14 or with /std:c++17), the following code works:
void TakePtr(char*); // const or not
int main()
{
TakePtr(char{});
TakePtr(char());
}
I don't understand why it works.
Apparently, the following would also work (as expected):
void TakeChar(char);
TakeChar(char{});
TakeChar(char());
How does the compiler deduce (or convert) the type char to char*, when char{} or char() is used as an argument?
Now, if I have both char and char* overloads, it works without any error/warning about ambiguity:
void TakePtr(char*);
void TakePtr(char);
TakePtr(char{}); // Chooses 'char'
TakePtr(char()); // Chooses 'char'
Why is the compiler okay with char{} for TakePtr(char*)?
And why doesn't it give a warning/error when choosing the better version? Such behavior is bound to break existing code.
For sure, the compiler isn't happy with:
void TakePtr(char*);
char c{};
TakePtr(c);
Because Visual lies a lot. Especially older one. Your code prompts clang to report an error:
<source>:9:6: error: no matching function for call to 'TakePtr'
TakePtr(char{});
^~~~~~~
<source>:5:6: note: candidate function not viable: no known conversion from 'char' to 'char *' for 1st argument
void TakePtr(char*); // const or not
^
<source>:10:6: error: no matching function for call to 'TakePtr'
TakePtr(char());
^~~~~~~
<source>:5:6: note: candidate function not viable: no known conversion from 'char' to 'char *' for 1st argument
void TakePtr(char*); // const or not
^
2 errors generated.
Visual is known to be "wonky" in term of following C++ standard, so don't rely on it too much. Try to verify with clang / gcc, just to be sure.
This is simply MSVC being behind: the rule in C++03 was that any constant expression of integer type and value 0 was a null pointer constant and could thus be converted to char*. Certainly char() qualifies—and char{} means the same thing, although it never overlapped with the rule.
I have the struct student and I did not declare a constructor. What will happen if I do the following?
struct student{
int assns, mt, finalExam;
float grade(){…}
}
student billy (60, 70, 80);
This answer is written according to the question heading, and not the body, as they seem to be gravely conflicting, hope the OP edits this.
You will encounter a error during compile time.
Code:
#include <iostream>
class test
{
int tt;
};
int main ()
{
test t1 (34);
}
Compiler Error:
In function 'int main()':
10:17: error: no matching function for call to 'test::test(int)' 10:17: note: candidates are:
2:7: note: test::test()
2:7: note: candidate expects 0 arguments, 1 provided
2:7: note: constexpr test::test(const test&)
2:7: note: no known conversion for argument 1 from 'int' to 'const test&'
2:7: note: constexpr test::test(test&&)
2:7: note: no known conversion for argument 1 from 'int' to 'test&&'
This happens as there is no constructor defined which takes a parameter. Without the ctor there is no meaning of class, as you can never initialize its data member, and how can you expect something to be constructed if the construction company itself is absent.
The compiler will throw error.
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)...);
}
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.