GCC problem with static_cast<std::u16string> - c++

Summary
I have a class to which I've added a typecast operator to convert to std::u16string. The signature of this operator looks as follows:
operator const std::u16string() const;
In my .cpp file, I try converting an object of my class's type to std::u16string as follows:
std::u16string sUTF16Password = static_cast<std::u16string>(Password_);
On Visual Studio 2017, this works just fine. However, GCC 6.3 on my Raspberry Pi gives the following error at compile time:
error : call of overloaded 'basic_string(MyClass&)' is ambiguous
What is the right way to write this typecast? Searching on Google brings a lot of hits for character encoding conversions, but that's not my problem here. I don't understand why the basic_string constructor is being called here despite using static_cast.
Full example
Here's a minimal example. Compiling it with g++ main.cpp fails on my Raspberry Pi.
#include <iostream>
#include <string>
class MyClass
{
private:
std::u16string Str;
public:
MyClass() { Str = u"abcd"; }
operator const char16_t*() const { return Str.c_str(); }
operator std::u16string() const { return Str; }
};
int main()
{
MyClass Tester;
std::u16string TestStr = static_cast<std::u16string>(Tester);
for (size_t idx = 0; idx < TestStr.size(); idx++)
std::cout << idx << ": " << TestStr[idx] << std::endl;
return 0;
}
The output of gcc --version is gcc (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516.
The full output from g++ main.cpp is:
main.cpp: In function ‘int main()’:
main.cpp:17:61: error: call of overloaded ‘basic_string(MyClass&)’ is ambiguous
std::u16string TestStr = static_cast<std::u16string>(Tester);
^
In file included from /usr/include/c++/6/string:52:0,
from /usr/include/c++/6/bits/locale_classes.h:40,
from /usr/include/c++/6/bits/ios_base.h:41,
from /usr/include/c++/6/ios:42,
from /usr/include/c++/6/ostream:38,
from /usr/include/c++/6/iostream:39,
from main.cpp:1:
/usr/include/c++/6/bits/basic_string.h:476:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_
CharT, _Traits, _Alloc>&&) [with _CharT = char16_t; _Traits = std::char_traits<char16_t>; _Alloc = std::allocator<char16_t>]
basic_string(basic_string&& __str) noexcept
^~~~~~~~~~~~
/usr/include/c++/6/bits/basic_string.h:454:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&
) [with _CharT = char16_t; _Traits = std::char_traits<char16_t>; _Alloc = std::allocator<char16_t>]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())
^~~~~~~~~~~~
/usr/include/c++/6/bits/basic_string.h:397:7: note: candidate: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_st
ring<_CharT, _Traits, _Alloc>&) [with _CharT = char16_t; _Traits = std::char_traits<char16_t>; _Alloc = std::allocator<char16_t>]
basic_string(const basic_string& __str)
^~~~~~~~~~~~
If I remove the typecast to const char16_t* this example compiles just fine. I still don't understand why having both typecasts is a problem.

If you compile as C++14 (or earlier), you'll get this ambiguous call, because std::u16string(char16_t*) participates in the overload resolution (via MyClass::operator const char16_t*()) as well as MyClass::operator std::u16string() that would appear to be a better match.
This can be overcome in several ways:
Compile as C++17 (or later) with GCC 7 or later (sadly, this doesn't help with GCC 6).
Remove operator const char16_t*().
Add explicit to operator const char16_t*() (or to both conversion operators).

Related

C++ user-defined conversions, ref-qualifiers and overload resolution

Please, help me understand what's wrong with this piece of code:
#include <string>
#include <utility>
class Sample
{
public:
explicit Sample(std::string data):
_data(std::move(data))
{}
operator const std::string &() const & { return _data; }
operator std::string &&() && { return std::move(_data); }
private:
std::string _data;
};
int main()
{
auto sample1 = Sample{"a"};
const auto sample2 = Sample{"b"};
auto sample3 = Sample{"c"};
auto s1 = std::string{sample1};
auto s2 = std::string{sample2};
auto s3 = std::string{std::move(sample3)};
return 0;
}
The problem is with s3. The compiler says sth like that:
<source>: In function 'int main()':
<source>:27:45: error: call of overloaded 'basic_string(<brace-enclosed initializer list>)' is ambiguous
27 | auto s3 = std::string{std::move(sample3)};
| ^
In file included from /opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/string:55,
from <source>:1:
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:565:7: note: candidate: 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'
565 | basic_string(basic_string&& __str) noexcept
| ^~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/basic_string.h:456:7: note: candidate: 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'
456 | basic_string(const basic_string& __str)
| ^~~~~~~~~~~~
Compiler returned: 1
As I understand, both of the operators for some reason participate in overload resolution for an rvalue. Why is that happening? Should ref-qualifiers leave only one overload for lvalue and the other for rvalue? Am I missing something?
A few other notes:
The behaviour is different across different compilers/versions. I tested it with godbolt and here is what I found:
gcc 8.3 and older - compiles fine
gcc 8.4 and newer - fails to compile
clang - I couldn't find a version where it compiles
MSVC - compiles fine on newest versions
if I refactor operators to normal member functions, it all works as intended
Thank You for Your time, looking forward to any replies :)

C++ Trying to display data of different types using a template output function

I have made a function that is supposed to output data types which include ints, chars, strings and a class called item. This is the function:
template < typename T >
void Output(const T* first, const T* last, char outforchar= '\0')
{
if (outforchar== '\0')
{
std::for_each(first, last, [](T i){std::cout << i;});
}
else
{
std::for_each(first, last, [outforchar](T i){std::cout << i << outforchar;});
}
}
While this function works for types like ints, chars, and strings. It doesn't work for the item class which has a >> operator which formats the input according to the specifications. The compiler throws this error:
./tempsorter.h:14:47: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
std::for_each(beg, end, [](T i){std::cout << i;});
^
In file included from /usr/include/c++/4.8.2/iostream:39:0,
from psort.cpp:16:
/usr/include/c++/4.8.2/ostream:602:5: error: initializing argument 1 of ‘std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = Product]’
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
In file included from /usr/include/c++/4.8.2/algorithm:62:0,
from ./tempsorter.h:2,
from psort.cpp:18:
/usr/include/c++/4.8.2/bits/stl_algo.h:4411:5: error: ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = const Item*; _Funct = Output(const T*, const T*, char) [with T = Item]::__lambda0]’, declared using local type ‘Output(const T*, const T*, char) [with T = Item]::__lambda0’, is used but never defined [-fpermissive]
for_each(_InputIterator __first, _InputIterator __last, _Function __f)
^
/usr/include/c++/4.8.2/bits/stl_algo.h:4411:5: error: ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = const Item*; _Funct = Output(const T*, const T*, char) [with T = Item]::__lambda1]’, declared using local type ‘Output(const T*, const T*, char) [with T = Item]::__lambda1’, is used but never defined [-fpermissive]
I understand this error and can get rid of it using something like:
for (auto i = first; i != last; ++i)
{
std::cout << i;
}
But then this gives me incorrect outputs so I can't use it at all.
I am expected to traverse through the various types with something like the following:
for (typename E::ConsItr i = e.Begin(); i != e.End(); ++i)
I can't seem to understand how to implement this into the function, what am I doing wrong?
First,
[](T const& i){std::cout << i;}
is probably better. In addition, you probably wrote your << operator wrong.
std::ostream& operator<<(std::ostream& os, const SomeType& x)
it should look like that, and possibly be a friend of your type. Ensure it is in the same namespace as your type. Or you overloades >> instead of <<

calling std::minus can't work after overloading - operator

Am getting compilation errors for the following snippet of my C++ code.
struct Power{
int power;
int age;
int operator-(const Power& p1)
{
return this->power - p1.power;
}
};
int main() {
Power p1;
p1.power = 1;
p1.age = 25;
Power p2;
p2.power = 2;
p2.age = 26;
std::cout<<std::minus<Power>()(p1, p2)<<std::endl;
}
build with c++11. cannot be built.
error messages are:
error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘Power’)
std::cout<<std::minus<Power>()(p1, p2)<<std::endl;
^
In file included from /usr/include/c++/5/iostream:39:0,
from rvaluereference.cpp:1:
/usr/include/c++/5/ostream:628:5: note: candidate: std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = Power] <near match>
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
/usr/include/c++/5/ostream:628:5: note: conversion of argument 1 would be ill-formed:
rvaluereference.cpp:60:39: error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
.....
std::minus has a single template parameter that defines the type of both inputs and the output; it doesn't handle type switching AFAICT. Your operator- takes Power, and returns int, but std::minus<Power> must take and return Power. The complaint is because std::minus<Power> returns Power, and ostream doesn't have an overload for operator<< that accepts Power.
As noted in the comments, if you can use C++14, std::minus<void> accepts mismatched arguments and deduces the return type, so if you can use C++14, that's another option.
While std::minus only can do T operator-(const T &lhs, const T &rhs), your operators do not have the same limitations. Declare the operator outside of the class:
int operator-(const Power &lhs, const Power &rhs) {
return lhs.power - rhs.power;
}
and now you can do std::cout << p1 - p2 << std::endl;

Outputting user defined structure with boost::log

I'm trying to output content of boost::asio::streambuf object with boost::log library. I defined operator<< overload in the following way:
ostream& operator<<(ostream& ostr, const boost::asio::streambuf& buffer)
{
for (size_t i = 0; i < buffer.size(); ++i)
{
ostr << hex << (int) buffer_cast<const char*>(buffer.data())[i] << " ";
}
return ostr;
}
But after trying to output the content of the buffer:
BOOST_LOG_TRIVIAL(trace) << buffer;
I have the following error:
In file included from
/home/bobeff/work/asio_netcomm_poc/third_party/lib/boost/boost/log/sources/record_ostream.hpp:31:0,
from /home/bobeff/work/asio_netcomm_poc/third_party/lib/boost/boost/log/trivial.hpp:23,
from /home/bobeff/work/asio_netcomm_poc/server/src/server.cpp:14:
/home/bobeff/work/asio_netcomm_poc/third_party/lib/boost/boost/log/utility/formatting_ostream.hpp:
In instantiation of 'typename
boost::log::v2_mt_posix::aux::enable_if_formatting_ostream::type boost::log::v2_mt_posix::operator<<(StreamT&, T&)
[with StreamT =
boost::log::v2_mt_posix::basic_formatting_ostream; T =
boost::asio::basic_streambuf<>; typename
boost::log::v2_mt_posix::aux::enable_if_formatting_ostream::type =
boost::log::v2_mt_posix::basic_formatting_ostream&]':
/home/bobeff/work/asio_netcomm_poc/third_party/lib/boost/boost/log/sources/record_ostream.hpp:212:51:
required from 'typename
boost::log::v2_mt_posix::aux::enable_if_record_ostream::type boost::log::v2_mt_posix::operator<<(StreamT&, T&)
[with StreamT = boost::log::v2_mt_posix::basic_record_ostream; T
= boost::asio::basic_streambuf<>; typename boost::log::v2_mt_posix::aux::enable_if_record_ostream::type =
boost::log::v2_mt_posix::basic_record_ostream&]'
/home/bobeff/work/asio_netcomm_poc/server/src/server.cpp:88:47:
required from here
/home/bobeff/work/asio_netcomm_poc/third_party/lib/boost/boost/log/utility/formatting_ostream.hpp:840:19:
error: cannot bind
'boost::log::v2_mt_posix::basic_formatting_ostream::ostream_type
{aka std::basic_ostream}' lvalue to 'std::basic_ostream&&'
strm.stream() << value;
^ In file included from /usr/include/c++/4.8/iostream:39:0,
from /home/bobeff/work/asio_netcomm_poc/server/src/server.cpp:1:
/usr/include/c++/4.8/ostream:602:5: error: initializing argument 1
of 'std::basic_ostream<_CharT, _Traits>&
std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&)
[with _CharT = char; _Traits = std::char_traits; _Tp =
boost::asio::basic_streambuf<>]'
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
What is the right way to output the content of the buffer?
Your operator<< is not found by ADL. See the first part of this answer.

Is using a reference parameter with default value good practice?

I have the following code:
#include <string>
#include <iostream>
void f(const std::string& s = "")
{
std::cout << "\"" << s << "\"" << std::endl;
}
int main()
{
std::string s1 = "qwe";
f();
f("asd");
f(s1);
}
How bad (if at all) are the calls with the temporary and without the parameters?
As far as I know this compiles only due to the fact that const reference prolongs the life of the temporary until the end of a method http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Trying to compile the same example without the const next to s parameter fails.
#include <string>
#include <iostream>
void f(std::string& s = "")
{
std::cout << "\"" << s << "\"" << std::endl;
}
int main()
{
std::string s1 = "qwe";
f();
f("asd");
f(s1);
}
Compilation
g++-5 -O3 -Wall --std=c++11 main.cpp && ./a.out
main.cpp:4:27: error: invalid initialization of non-const reference of type ‘std::string& {aka std::basic_string<char>&}’ from an rvalue of type ‘std::string {aka std::basic_string<char>}’
void f(std::string& s = "")
^
In file included from /usr/include/c++/5/string:52:0,
from main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:2893:7: note: after user-defined conversion: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
^
main.cpp: In function ‘int main()’:
main.cpp:12:5: error: invalid initialization of non-const reference of type ‘std::string& {aka std::basic_string<char>&}’ from an rvalue of type ‘std::string {aka std::basic_string<char>}’
f();
^
In file included from /usr/include/c++/5/string:52:0,
from main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:2893:7: note: after user-defined conversion: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
^
main.cpp:4:6: note: in passing argument 1 of ‘void f(std::string&)’
void f(std::string& s = "")
^
main.cpp:13:10: error: invalid initialization of non-const reference of type ‘std::string& {aka std::basic_string<char>&}’ from an rvalue of type ‘std::string {aka std::basic_string<char>}’
f("asd");
^
In file included from /usr/include/c++/5/string:52:0,
from main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:2893:7: note: after user-defined conversion: std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
^
main.cpp:4:6: note: initializing argument 1 of ‘void f(std::string&)’
void f(std::string& s = "")
It's not a horrible practice, but it's generally better to provide overloads:
void f(std::string const& s) { std::cout << "\\" << s << "\\\n"; }
void f() { f(""); }
It avoids some language features that end up being confusing to many people. For example, what does this print?
struct base { virtual void f(int i = 42) { std::cout << i; } };
struct derived : base { void f(int i = 19) { std::cout << i; }};
int main() { base * b = new derived(); b->f(); }
There are also ambiguity errors that can come up when you're using default parameters that don't when you use overloads.
As far as const references in particular, that doesn't really matter much. The default value binds to reference for the lifetime of the function call. It has no effect at all really. You might get better results using values sometimes when the compiler can perform certain optimizations that are not possible with reference parameters, but generally it's not something to be concerned with.
Of course, this doesn't work with non-const references because they don't bind to temporaries.
The default value is for allowing you to call function without any arguments, so use reference parameter with default value if you sometimes will not pass argument to function, otherwise there is no matter to do so.