implicit conversion operator - c++

I want to print all the arguments of function using variadic templates feature of C++11. And I did the following:
struct concatenate
{
template< typename ...ARGS >
explicit
concatenate(ARGS const & ...args)
{
cat(args...);
}
/*explicit*/
operator std::string const () const
{
return oss.str();
}
private :
std::ostringstream oss;
void cat() const
{ ; }
template< typename T, typename ...ARGS >
void cat(T const & head, ARGS const & ...tail)
{
if (oss.tellp() > 0) {
oss << ' ';
}
oss << head;
cat(tail...);
}
};
Then I try to test it:
std::cout << '\'' << concatenate(1, 2, 3, 4, std::string("ololo"), "alala", 'o', 1.2, 1.2L, 1.2f) << '\'' << std::endl;
but then given the code does not compile with error:
error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'
c:\mingw\lib\gcc\mingw32\4.7.0\include\c++\ostream:600: 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 = concatenate]'
What is the nature of the error? After all, the compiler has no choice but to use a conversion operator. It isn't?

operator << for class template std::basic_string is defined as (free) function template. In contrast with non-template functions, template argument deduction doesn't involve possible argument conversions. Templated operator << for basic_string needs exactly a string as it right argument and implicit conversion (from concatenate to string) doesn't work.

Because you're not using the explicit conversion operator and you have not overloaded operator<< for your class it tries to call:
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);
Using the explicit conversion
std::string(concatenate(1, 2, 3, 4, std::string("ololo"), "alala", 'o', 1.2, 1.2L, 1.2f))
does the right thing.

Related

Why can't the compiler use the std::string conversion function of the class when perform operator<<?

Consider the following struct with a user-defined conversion function that can convert itself to const char*;
struct S {
operator const char*() { return "hello"; }
};
This work with <iostream>, we can print the struct S with no error message:
std::cout << S{} << '\n';
But if I change the return type to std::string:
struct S {
operator std::string() { return "hello"; }
};
I got this compiler error message:
<source>:11:13: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'S')
11 | std::cout << S{} << '\n';
| ~~~~~~~~~ ^~ ~~~
| | |
| | S
| std::ostream {aka std::basic_ostream<char>}
<source>:11:18: note: 'S' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
11 | std::cout << S{} << '\n';
| ^
Why can't the compiler use the std::string conversion? Is there a difference between the conversion function of the built-in and class type?
Because operator<< for std::basic_string is a template taking 3 template parameters:
template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::basic_string<CharT, Traits, Allocator>& str);
And implicit conversion won't be considered in template argument deduction:
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Then given std::cout << S{};, the template parameter CharT, Traits and Allocator can't be deduced on the 2nd function argument.
On the other hand, operator<< for const char* doesn't have such issue; given std::cout << S{};, the template parameter CharT and Traits would be deduced only from the 1st function argument. After deduction, the implicit conversion from S to const char* will be performed and the calling works well.

std::make_pair type deduction

I came across some odd thing I would like to have an explanation for. The following code snippet provides a simple class template type and two operator<<s: one for specializations of type and one for a std::pair of type specializations.
#include <ostream>
#include <utility>
template <typename T>
class type {
public:
T value_;
};
template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, type<T> const& a)
{
return os << a.value_;
}
template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T const, T const> const& a)
{
return os << a.first << ',' << a.second;
}
#include <iostream>
int
main()
{
using float_type = type<float>;
float_type const a = { 3.14159 };
float_type const b = { 2.71828 };
#if 0
std::cout << std::make_pair(a, b)
<< std::endl;
#else
std::cout << std::pair<float_type const, float_type const>(a, b)
<< std::endl;
#endif
}
The main function provides a specialization and two variables of that specialization. There are two variants for displaying the variables as a std::pair. The first fails because std::make_pair seems to strip the const specifier from the variables, which in turn doesn't match with the signature of the second operator<<: std::pair<T const, T const>. However, constructing a std::pair specialization (second std::cout line in main) works as well as removing the const specification for T from operator<< for std::pair, i.e. std::pair<T, T>.
Compiler messages::
gcc 4.9.2
std_make_pair.cpp: In function 'int main()':
std_make_pair.cpp:52:35: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
std::cout << std::make_pair(a, b) << std::endl;
^
In file included from std_make_pair.cpp:3:0:
/usr/include/c++/4.9.2/ostream:602:5: note: 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 = std::pair<type<float>, type<float> >]'
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
clang 3.5 (non-viable functions from system headers removed)
std_make_pair.cpp:52:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>')
and 'pair<typename __decay_and_strip<const type<float> &>::__type, typename __decay_and_strip<const
type<float> &>::__type>')
std::cout << std::make_pair(a, b) << std::endl;
~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~
std_make_pair.cpp:30:1: note: candidate template ignored: can't deduce a type for 'T' which would make
'const T' equal 'type<float>'
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T const, T const> const& a)
so, here's the question: am I supposed to specify an operator<< taking a std::pair of T instead of T const? Isn't that watering down the contract I'm setting up with any user of the functionality, i.e. with T const I basically promise to use T only in non-mutating ways?
The first fails because std::make_pair seems to strip the const specifier from the variables, which in turn doesn't match with the signature of the second operator<<: std::pair<T const, T const>
That is correct. make_pair is a function template that relies on std::decay to explicitly drop const, volatile, and & qualifiers:
template <class T1, class T2>
constexpr pair<V1, V2> make_pair(T1&& x, T2&& y);
Returns: pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y));
where V1 and V2 are determined as follows: Let Ui be decay_t<Ti> for each Ti. Then each Vi is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.
The compiler is completely correct to reject your code - you added a stream operator for pair<const T, const T>, but are trying to stream a pair<T, T>. The solution is to just remove the extra const requirement in your stream operator. Nothing in that function requires that the pair consist of const types - just that the types themselves are streamable, which is independent of their constness. There is nothing wrong with this:
template <typename CTy, typename CTr, typename T>
std::basic_ostream<CTy,CTr>&
operator<<(std::basic_ostream<CTy,CTr>& os, std::pair<T, T> const& a)
{
return os << a.first << ',' << a.second;
}
You're already taking the pair by reference-to-const, it's not like you can modify its contents anyway.

How to use enable_if to conditionally define a << operator

I'm trying to define a << operator for a set of classes; the set is open, but all of the members have a common tagging base class, and all have the member function std::string String() const. Basically, what I've got is:
class Tag {};
class Obj : public Tag
{
public:
std::string String() const { return "specialized"; }
};
template <typename T>
typename std::enable_if<std::is_base_of<Tag, T>::type, std::ostream>::value& operator<<( std::ostream& dest, T const& source)
{
dest << source.String();
return dest;
}
int
main()
{
std::cout << typeid(std::enable_if<std::is_base_of<Tag, Obj>::value, std::ostream>::type).name() << std::endl;
std::string s( "generic" );
Obj e;
std::cout << e << std::endl;
std::cout << s << std::endl;
return 0;
}
This doesn't work: with g++ (version 4.8.3, invoked with -std=c++11), I get the error message:
enableIf.cc: In function 'int main()':
enableIf.cc:55:18: error: cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'
std::cout << e << std::endl;
^
In file included from /usr/lib/gcc/x86_64-pc-cygwin/4.8.3/include/c++/iostream:39:0,
from enableIf.cc:8:
/usr/lib/gcc/x86_64-pc-cygwin/4.8.3/include/c++/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 = Obj]'
operator<<(basic_ostream<_CharT, _Traits>&& __os, const _Tp& __x)
^
I can't figure it out, because there aren't any rvalue-references in sight; the compiler seems to have struck on the generic overload for std::ostream&& in the standard library.
With MSC (VS 2013), the error message is a lot more verbose, but it starts with:
enableIf.cc(55) : error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Obj' (or there is no acceptable conversion)
and then goes on to list a lot of possible functions, all in the standard library.
(In my actual code, line 55 corresponds to the line std::cout << e << std::endl;.)
In both cases, the compiler seems to be rejecting my overloaded function. If I comment out the << lines, however, the code compiles, and the value output by the first line in main seems correct (at least with MSC—the output of g++ is So, what ever that's supposed to mean).
Given that two compilers agree, I assume that there is an error in my code, but I can't figure out what. How do you do this? (FWIW: I'd be equally happy, or even happier, with a solution which generates the overload for all types having a member function std::string Type::String() const.)
I'm pretty sure you meant this:
template <typename T> // here here
typename std::enable_if<std::is_base_of<Tag, T>::type, std::ostream>::value&
operator<<( std::ostream& dest, T const& source)
to be this:
template <typename T>
typename std::enable_if<std::is_base_of<Tag, T>::value, std::ostream>::type&
operator<<( std::ostream& dest, T const& source)
after changing as such, you compile successfully.

Conversion operators

This code is not compilable.
I can't find why in standard. Can someone explain?
#include <iostream>
#include <string>
template<typename T>
class S
{
public:
explicit S(const std::string& s_):s(s_)
{
}
std::ostream& print(std::ostream& os) const
{
os << s << std::endl;
return os;
}
private:
std::string s;
};
template<typename T>
std::ostream& operator << (std::ostream& os, const S<T>& obj)
{
return obj.print(os);
}
/*template<>
std::ostream& operator << <std::string> (std::ostream& os, const S<std::string>& obj)
{
return obj.print(os);
}*/
class Test
{
public:
explicit Test(const std::string& s_):s(s_)
{
}
//operator std::string() const { return s; }
operator S<std::string>() const { return S<std::string>(s); }
private:
std::string s;
};
int main()
{
Test t("Hello");
std::cout << t << std::endl;
}
Compiler output:
source.cpp: In function 'int main()':
source.cpp:47:17: error: no match for 'operator<<' in 'std::cout << t'
source.cpp:47:17: note: candidates are:
In file included from include/c++/4.7.1/iostream:40:0,
from source.cpp:1:
include/c++/4.7.1/ostream:106:7: note: std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>::__ostream_type& (*)(std::basic_ostream<_CharT, _Traits>::__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_ostream<_CharT, _Traits>::__ostream_type = std::basic_ostream<char>]
include/c++/4.7.1/ostream:106:7: note: no known conversion for argument 1 from 'Test' to 'std::basic_ostream<char>::__ostream_type& (*)(std::basic_ostream<char>::__ostream_type&) {aka std::basic_ostream<char>& (*)(std::basic_ostream<char>&)}'
....
Thats because no conversions, except for array-to-pointer, function-to-pointer, lvalue-to-rvalue and top-level const/volatile removal (cf. c++11 or c++03, 14.8.2.1), are considered when matching a template function. Specifically, your user-defined conversion operator Test -> S<string> is not considered when deducing T for your operator<< overload, and that fails.
To make this universal overload work, you must do all the work at the receiving side:
template <class T>
typename enable_if<is_S<T>::value, ostream&>::type operator <<(ostream&, const T&);
That overload would take any T, if it weren't for the enable_if (it would be unfortunate, since we don't want it to interfere with other operator<< overloads). is_S would be a traits type that would tell you that T is in fact S<...>.
Plus, there's no way the compiler can guess (or at least it doesn't try) that you intended to convert Test to a S<string> and not S<void> or whatever (this conversion could be enabled by eg. a converting constructor in S). So you have to specify that
Test is (convertible to) an S too
the template parameter of S, when converting a Test, is string
template <class T>
struct is_S {
static const bool value = false;
};
template <class T>
struct is_S<S<T>> {
static const bool value = true;
typedef T T_type;
};
template <>
struct is_S<Test> {
static const bool value = true;
typedef string T_type;
};
You will have to convert the T to the correct S manually in the operator<< overload (eg. S<typename is_S<T>::T_type> s = t, or, if you want to avoid unnecessary copying, const S<typename is_S<T>::T_type> &s = t).
The first paragraph of #jpalecek's answer explains what the issue is. If you need a workaround, you could add a declaration like:
inline std::ostream& operator<< (std::ostream& os, const S<std::string>& s)
{ return operator<< <> (os, s); }
Since that overload is not a template, implicit conversions to S<std::string> will be considered.
But I can't see any way to do this for all types S<T>...

Why can't gcc deduce templated size for array argument? (C++11)

The following code gives a compiler error (gcc-4.7 run with -std=c++11):
#include <iostream>
#include <array>
template <typename T, int N>
std::ostream & operator <<(std::ostream & os, const std::array<T, N> & arr) {
int i;
for (i=0; i<N-1; ++i)
os << arr[i] << " ";
os << arr[i];
return os;
}
int main() {
std::array<double, 2> lower{1.0, 1.0};
std::cout << lower << std::endl;
return 0;
}
Error message:
tmp6.cpp: In function ‘int main()’: tmp6.cpp:16:16: error: cannot bind
‘std::ostream {aka std::basic_ostream}’ lvalue to
‘std::basic_ostream&&’ In file included from
/usr/include/c++/4.7/iostream:40:0,
from tmp6.cpp:1: /usr/include/c++/4.7/ostream:600: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 = std::array]’
When I get rid of the template function declaration and replace T with double and N with 2, it compiles just fine (edit: leaving T and replacing N with 2 works, but specifying N=2 as the default argument for N doesn't work.).
Does anyone know why gcc can't automatically bind this?
What would be the syntax for calling the << operator with explicitly specified template parameters?
Answer to question 2: operator<<<double, 2>(std::cout, lower);
Edit: This is also true for the following function, which is only templated in the array size:
template <int N>
void print(const std::array<double, N> & arr) {
std::cout << "print array here" << std::endl;
}
int main() {
std::array<double, 2> lower{1.0, 1.0};
print<2>(lower); // this works
print(lower); // this does NOT work
return 0;
}
Thanks a lot for your help.
Consider your declaration:
template <typename T, int N>
std::ostream & operator <<(std::ostream & os, const std::array<T, N> & arr) {
The definition for std::array is:
template<typename T, std::size_t N> class array {...};
You are using int instead of std::size_t, and that's why it doesn't match.
You can call operator<< with specified template parameters in this, not so aestethic, way:
operator<< <double,2>(std::cout, lower) << std::endl;