Why does VC++ compile this code while Clang won't - c++

https://godbolt.org/g/kNlYxl
Clang version: X86-64 clang 3.9.1
VC++ version: x86-64 CL 19 RC
I would expect that it would compile since const char* is implicitely convertible to A and A is convertible to B. The interesting thing is that clang states that const char [5] is not convertible to A? Note: i understand now its not standard behavior, but i would still like to know the reason why VC++ accepts this code, i.e. which language extension is causing it?
Error given by clang:
no viable conversion from 'const char [5]' to 'B'
Hints given by clang:
note: candidate constructor not viable: no known conversion from 'const char [5]' to 'A' for 1st argument
note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'const char [5]' to 'const B &' for 1st argument
#include <string>
#include <vector>
struct A
{
std::string m_test;
A(const char* test)
: m_test(test)
{
}
};
struct B
{
A m_a;
B( A a )
: m_a(a)
{
}
};
int main()
{
B test = "test";
}

Only one implicit user-defined conversion is allowed, see [class.conv]/4:
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
So it seems to be a Microsoft C++ extension, indeed, if you disable MSVC extensions (/Za), you'll get the same error:
error C2440: 'initializing': cannot convert from 'const char [5]' to 'B'
As to the reason why - it looks like some kind of a "multiple implicit conversions" extension, but there is no mention of it in documentation. There was even a bug submitted, and it was supposed to be fixed, but I guess that didn't work out.

Related

Why do char{} and char() work as a temporary variable for a char* argument?

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.

Why does adding a temp variable here make this compiler error go away? The types are the same

Given the following:
#include <string>
class s1
{
private:
std::string storage;
public:
s1(s1 *newValue) { storage=*newValue; }
operator std::string () { return storage; }
};
class s2
{
private:
std::string storage;
public:
s2(s2 *newValue) { std::string temp=*newValue; storage=temp; }
operator std::string const () { return storage; }
};
class s3
{
private:
std::string storage;
public:
s3(s3 *newValue) { storage=*newValue; } // Compile Error here.
operator std::string const () { return storage; }
};
The first (s1) and second (s2) examples compile fine; the third one does not... but s2 and s3 should be identical except for passing the value thru the temp variable.
Chasing the error messages into the library code, I think that the compiler is trying to bind to a move constructor on std::string in the case of s3.
What I don't understand is why it's doing that; and why I can do what I did with s2, but not s3 --- shouldn't they be the same, semantically?
Any enlightenment anybody can provide would be MUCH appreciated!
FWIW, the IDE is Xcode Version 9.2 (9C40b); and the C++ variant is gnu++17. Also, here's the (slightly redacted) error listing from Xcode:
In file included from /Users/... big long path here... .cpp:9:
/Users/... big long path here... .hpp:77:31: error: no viable conversion from 's3' to 'std::__1::basic_string<char>'
s3(s3 *newValue) { storage=*newValue; }
^~~~~~~~~
In file included from /Users/... big long path here... .cpp:9:
In file included from /Users/... big long path here... .hpp:45:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:763:5: note: candidate constructor not viable: no known conversion from 's3' to 'const std::__1::basic_string<char> &' for 1st argument
basic_string(const basic_string& __str);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:768:5: note: candidate constructor not viable: no known conversion from 's3' to 'std::__1::basic_string<char> &&' for 1st argument
basic_string(basic_string&& __str)
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:778:31: note: candidate constructor not viable: no known conversion from 's3' to 'const value_type *' (aka 'const char *') for 1st argument
_LIBCPP_INLINE_VISIBILITY basic_string(const value_type* __s);
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:810:5: note: candidate constructor not viable: no known conversion from 's3' to 'initializer_list<value_type>' (aka 'initializer_list<char>') for 1st argument
basic_string(initializer_list<value_type> __il);
^
In file included from /Users/... big long path here... .cpp:9:
/Users/... big long path here... .hpp:78:4: note: candidate function
operator std::string const () { return storage; }
^
In file included from /Users/... big long path here... .cpp:9:
In file included from /Users/... big long path here... .hpp:45:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/string:829:44: note: passing argument to parameter '__str' here
basic_string& operator=(basic_string&& __str)
^
1 error generated.
The resolution of issue CWG 1604 makes it ill-formed to direct-initialize a rvalue reference of class type T with an initializer that can be converted to a const T rvalue, but an implicit conversion sequence can still be formed because the resolution does not change relevant part about implicit conversion sequence.
As a result, in your example of s3, while ranking string::operator=(const string&) and string::operator=(string&&) during overload resolution, according to [over.ics.rank]/3.2.3:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
...
S1 and S2 are reference bindings and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference
...
the compiler chooses string::operator=(string&&) finally, thus causes an error when the initialization of its parameter actually occurs.
In your example of s2, the semantic is copy-initializing rather than choosing proper assignment operator, and there is no reference binding. So everything is OK.
Thank T.C. for pointing out that this is already a drafting issue CWG 2077 in this discussion.
I would surmise (but it's only going on what your seeing):
s2(s2 *newValue) { std::string temp=*newValue; storage=temp; }
Can be completed automatically by the compiler via implicit casting.
Where as:
s3(s3 *newValue) { storage=*newValue; }
Cannot.
First place (*newValue) in brackets and see if its misinterpreting your intentions in the compiler. Second try performing an explicit cast:
s3(s3 *newValue) { storage=((std::string)(*newValue)); }
If neither of those work, I would have to conquer its a compiler issue.

Constructing a std::string_view from a braced std::string, clang and gcc disagree with -Wconversion

GCC 7.2 and clang 5.0 seem to disagree about the following code when compiled with -std=c++17 -Werror -Wconversion:
#include <iostream>
#include <string>
#include <string_view>
int main(int argc, char* argv[]) {
std::string s = "Hello, World";
std::string_view vs{s};
std::cout << std::string{vs} << '\n';
return EXIT_SUCCESS;
}
Compiled with clang++ -std=c++17 -Wall -Werror -Wconversion, it compiles correctly (see https://godbolt.org/g/JZn8cL)
However, GCC 7.2 issues a somewhat perplexing diagnostic (see https://godbolt.org/g/kmYbJ3):
<source>: In function 'int main(int, char**)':
7 : <source>:7:26: error: choosing 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type = std::basic_string_view<char>]' over 'constexpr std::basic_string_view<_CharT, _Traits>::basic_string_view(const std::basic_string_view<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]' [-Werror=conversion]
std::string_view vs{s};
^
7 : <source>:7:26: error: for conversion from 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'std::basic_string_view<char>' [-Werror=conversion]
7 : <source>:7:26: note: because conversion sequence for the argument is better
cc1plus: all warnings being treated as errors
Compiler exited with result code 1
Both compilers compile the code fine after changing vs{s} to vs(s).
Which compiler is right here? Is brace initialization of a std::string_view from a std::string running afoul of conversion rules somehow, and clang is failing to issue a diagnostic when it could/should? Or is GCC in the wrong and the diagnostic is erroneous?
Which compiler is right here?
Both? Compilers are allowed to give warnings, or not, as they choose. It's just that when you write:
std::string_view vs{s};
You might be under the impression that this is doing something like aggregate initialization or at least invoking a string_view constructor, but it's not - and maybe that impression isn't as likely to happen if you wrote:
std::string_view vs(s);
so gcc gives you a warning (since you asked for one).
The code is fine. Both compilers are fine. Just use ()s to initialize, there's no reason to use {} here (there is no difference in behavior in the two approaches).
Your original question ("is this invalid C++?") has now been answered ("no."), but it's clear from the comments you're now interested in why gcc warns about this valid code. That is a more interesting question.
Removing the template arguments, the warning reduces to this:
warning: choosing 'std::string::operator std::string_view()' over 'constexpr std::string_view(const std::string_view&) ' [-Wconversion]
So it's warning about a user-defined conversion operator being chosen over a constructor... but that constructor couldn't actually be used for conversion. Here is another example that warns for the same reason:
class A { // Like string_view
public:
A() {}
};
class B { // Like string
public:
operator A() {return A();}
};
int main() {
B b;
A a{b};
}
This gives:
<source>: In function 'int main()':
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(A&&)' [-Wconversion]
A a{b};
^
12 : <source>:12:10: warning: for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note: because conversion sequence for the argument is better
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(const A&)' [-Wconversion]
12 : <source>:12:10: warning: for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note: because conversion sequence for the argument is better
By the way, adding e.g. A(int) constructor does not add anything to the warning, so it is focused on the move constructor and copy constructor of A. Deleting the copy and move constructors with =delete does not change the warning either.
This clarifies what is causing the warning, but not why it warns. The gcc documentation for -Wconversion says:
Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like abs (x) when x is double; conversions between signed and unsigned, like unsigned ui = -1; and conversions to smaller types, like sqrtf (M_PI). Do not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or if the value is not changed by the conversion like in abs (2.0). Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion.
For C++, also warn for confusing overload resolution for user-defined conversions; and conversions that never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.
So it depends on what you consider to be "confusing overload resolution for user-defined conversions". Perhaps the gcc devs consider A a{b} to look like a call to a constructor of A, and so they consider it calling B::operator A() to be "confusing".
Or perhaps it is indeed a bug... Given that this seems to be a rarely-used warning flag (it's not even in -Wall), and given how strange the "choosing x over y" messages are, that is quite possible.

What does this mean? : note: no known conversion for argument 1 from ‘int’ to ‘const account&’

I'm trying to understand the meaning of the errors that we generally face in out C++ programs.
While compiling a program I got a error (I did this error intentionally, please don't tell that how to correct that) and there a note is present which is :
note: no known conversion for argument 1 from ‘int’ to ‘const account&’
I want to understand the meaning of this note.
My program is :
#include<iostream>
class account
{
private:
int a_no;
public:
account()
{
a_no = 0;
}
void showData()
{
std::cout<<"\n account number = "<<a_no<<std::endl;
}
};
int main()
{
account a1;
a1.showData();
account a2(2);
a2.showData();
return 0;
}
I know that I haven't defined a constructor which can take one argument and doing that will remove my error.
Okay, while compiling this I got:
file1.cpp: In function ‘int main()’:
file1.cpp:20:17: error: no matching function for call to ‘account::account(int)’
account a2(2);
^
file1.cpp:20:17: note: candidates are:
file1.cpp:7:9: note: account::account()
account()
^
file1.cpp:7:9: note: candidate expects 0 arguments, 1 provided
file1.cpp:2:7: note: account::account(const account&)
class account
^
file1.cpp:2:7: note: no known conversion for argument 1 from ‘int’ to ‘const account&’
I want to know what is meaning of last line file1.cpp:2:7: note: no known conversion for argument 1 from ‘int’ to ‘const account&’ ?
1) You already know that you don't have an constructor that takes an int.
2) You know that you are trying to construct account with an int.
3) If you don't do it, compilers will create default copy-constructors, assignment-operators
4) The default copy-constructor takes a const reference to account
So what happens here? As there is only a default-constructor and you are constructing with one parameter the compiler thinks you want to copy-construct. As you are giving him an int as parameter for the copy-constructor, the compiler tries to convert the int to an account - which doesn't work, and he tells you about it: "no conversion possible from int to account"
This is very important to know as this is a source of many bugs. You propably didn't want to call the copy-constructor. But what happens if the compiler really finds a way to convert the type you used as a parameter to account? A mess....
I want to know what is meaning of last line file1.cpp:2:7: note: no known conversion for argument 1 from ‘int’ to ‘const account&’ ?
First, the message tell you
no matching function for call to ‘account::account(int)’
And there're two candidates, the 1st is the default ctor, but
file1.cpp:7:9: note: candidate expects 0 arguments, 1 provided
The 2nd is the copy ctor (implicitly generated), and its parameter's type is const account&, but
file1.cpp:2:7: note: no known conversion for argument 1 from ‘int’ to ‘const account&’
class constructor is a simple function as like as others, so when you send a int type to the function as parameter, you need to define parameter type for function :
class account
{
private:
int a_no;
public:
account(int a){ a_no = a; }
};
now, when you type account a2(2); int type defined for constructor and there isn't any problem

Move constructor and char array argument

struct Foo
{
char data[100];
template<int T>
Foo(char (&&var)[T])
{
data = std::move(var);
var = 0;
}
};
int main()
{
char v[100];
//..init v
Foo f( std::move(v) ); //error C2664: 'Foo::Foo<100>(char (&&)[100])' : cannot convert parameter 1 from 'char [100]' to 'char (&&)[100]'
return 0;
}
I don't get why MSVC is not happy with the line Foo f( std::move(v) ) ? (Maybe this code is senseless)
You could use the std::move algorithm, from the <algorithm> header.
auto it = std::move(data, data + T, var);
after checking that T isn't larger than 100. But moving char doesn't have many benefits over just copying.
The line
Foo f( std::move(v) );
is legal for GCC, Clang and Intel. Visual Studio however complains and yields
error C2664: 'Foo::Foo(const Foo &)' : cannot convert parameter 2 from 'char [100]' to 'char (&&)[100]'
1> You cannot bind an lvalue to an rvalue reference
This seems a bug to me. Indeed, v is an lvalue but std::move cast it to an rvalue reference.
Since arrays of chars are not movable, although legal, I don't see much use for taking them by rvalue reference. If you really want the code to compile, a workaround is making f taking a regular (lvalue) reference and remove the std::move at the call site. Or even better, make f take a pointer to char* (f doesn't need to be a template). In this case, calling f(v) passes the address of v[0] (i.e. the first element of v) to f. This is the so called array-to-pointer conversion which is implicitly performed by the compiler.
However, as others have pointed out, the lines
data = std::move(var);
var = 0;
are illegal for all compilers. data and var are arrays (or references to arrays) and arrays are not assignable. For instance, the error raised by var = 0; by each compiler is as follows:
GCC:
error: incompatible types in assignment of 'int' to 'char [100]'
Clang:
error: array type 'char [100]' is not assignable
Intel:
error: expression must be a modifiable lvalue
Visual Studio:
error C2440: '=' : cannot convert from 'int' to 'char [100]'