While looking at this question I tried it out with clang and got in a weird situation. The following example:
#include <string>
class ACP
{
public:
ACP() {}
operator const std::string() const { return std::string(); }
// operator std::string() const { return std::string(); } <-- makes clang happy
};
void test()
{
const ACP acp;
auto a = (std::string)acp;
}
compiles fine on coliru with gcc, but fails with clang. At least I see no issue with this example - is this a bug in clang or is there a rule that actually explains the clang error and gcc is wrong?
The error from clang can be seen below:
clang -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp:13:26: error: no viable conversion from 'const ACP' to 'std::__cxx11::basic_string<char>'
auto a = (std::string)acp;
^~~
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.1.0/../../../../include/c++/7.1.0/bits/basic_string.h:421:7: note: candidate constructor not viable: no known conversion from 'const ACP' to 'const std::__cxx11::basic_string<char> &' for 1st argument
basic_string(const basic_string& __str)
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.1.0/../../../../include/c++/7.1.0/bits/basic_string.h:493:7: note: candidate constructor not viable: no known conversion from 'const ACP' to 'const char *' for 1st argument
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.1.0/../../../../include/c++/7.1.0/bits/basic_string.h:515:7: note: candidate constructor not viable: no known conversion from 'const ACP' to 'std::__cxx11::basic_string<char> &&' for 1st argument
basic_string(basic_string&& __str) noexcept
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.1.0/../../../../include/c++/7.1.0/bits/basic_string.h:542:7: note: candidate constructor not viable: no known conversion from 'const ACP' to 'initializer_list<char>' for 1st argument
basic_string(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())
^
main.cpp:7:5: note: candidate function
operator const std::string() const { return std::string(); }
^
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/7.1.0/../../../../include/c++/7.1.0/bits/basic_string.h:515:35: note: passing argument to parameter '__str' here
basic_string(basic_string&& __str) noexcept
^
1 error generated.
However I can't see why the compiler could not use the copy ctor from std::string
.
Notice that clang also defines the GNU C version macros, but you should use the clang feature checking macros to detect the availability of various features. The values of the __clang_major__ , __clang_minor__ , and __clang_patchlevel__ macros are not consistent across distributions of the Clang compiler.
The Clang Compiler is an open-source compiler for the C family of programming languages, aiming to be the best in class implementation of these languages. Clang builds on the LLVM optimizer and code generator, allowing it to provide high-quality optimization and code generation support for many targets.
Clang Design: Like many other compilers design, Clang compiler has three phase: The front end that parses source code, checking it for errors, and builds a language-specific Abstract Syntax Tree (AST) to represent the input code. The optimizer: its goal is to do some optimization on the AST generated by the front end.
Simplified:
struct A {};
struct B { operator const A(); };
B b;
A a(b);
This is direct-initialization with destination type class type A
, so candidate constructors of A
are enumerated, selecting all the constructors of A (including the implicitly defined copy and move constructors) and compared by overload resolution for direct-initialization of a class object. First the constructors are assessed for viability, which means attempting to construct an implicit conversion sequence from b
(an lvalue B
) to their parameter. Since the parameter is a reference (A const&
or A&&
) we must perform a reference initialization. We can initialize a reference A const&
from b
since b
can be converted to an rvalue A const
, and A const
(the type of the target reference) and A const
(the return type of the target function) are reference-compatible (since they are the same cv-qualified type); indeed, this is a direct reference binding. We cannot initialize a reference A&&
from b
since A&&
has lesser cv-qualification than A const
. So A::A(A const&)
is the only viable constructor and is selected.
This has been reported to clang multiple times 1 2 3 but unfortunately hasn't been picked up.
All other major compilers (gcc, ICC, MSVC) compile the code correctly.
Interestingly, as discussed here clang compiles the code correctly in C++03 mode. If we force A
to be a "C++03-style" type by suppressing its move constructor, clang will compile the resulting code:
struct A { A(A const&) = default; };
struct B { operator const A(); };
B b;
A a(b);
This indicates that perhaps clang is getting confused by the move constructor. It shouldn't, since the move constructor is not viable; A&& a = b;
is invalid.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With