is there any mechanism that can be used to implement code as follows:
// T can be any type
std::function<T(int,int)> tf;
tf = [](int x, int y) -> int{
return x + y;
};
cout << tf(4, 5) << endl;
tf = [](int x, int y) -> string{
return "hello world";
}
cout << tf(4,5) << endl;
To solve this, we need T to:
Be able to type-erase and hold an instance of arbitrary type;
Be convertible from such an instance;
Overload the << operator and forward it to the type-erased instance dynamically.
Depending on whether your list of possible types is bounded or not, we can defer much of the heavy lifting to either boost::variant or boost::any (respectively std::variant or std::any in C++17 and above).
The variant version is straightforward:
template <class... Ts>
struct StreamableVariant : boost::variant<Ts...> {
using boost::variant<Ts...>::variant;
friend decltype(auto) operator << (std::ostream &os, StreamableVariant const &sv) {
return boost::apply_visitor([&](auto const &o) -> decltype(auto) {
return os << o;
}, sv);
}
};
// Usage
std::function<StreamableVariant<int, std::string>(int,int)> tf;
The any version is a bit more involved, as we need to type-erase the streaming functionality manually while we still know the object's type at construction time:
struct StreamableAny : boost::any {
template <class T>
StreamableAny(T &&t)
: boost::any{std::forward<T>(t)}
, _printMe{[](std::ostream &os, StreamableAny const &self) -> decltype(auto) {
return os << boost::any_cast<T const &>(self);
}}{ }
private:
friend std::ostream &operator << (std::ostream &os, StreamableAny const &sa) {
return sa._printMe(os, sa);
}
std::ostream &(*_printMe)(std::ostream &os, StreamableAny const &);
};
// Usage
std::function<StreamableAny(int,int)> tf;
You can't assign a callable with a return type different from the one initially used in the std::function unless the former is implicitly convertible to the latter. The assignment operator will not be a candidate.
There is another case in which the return types can differ and is when the return type of the std::function object is void:
std::function<void(int)> f = [](int) -> int { return 0; }
Related
Here is a (very distilled) use case and code example (sorry if it doesn't look too minimal, I couldn't figure out what else to exclude. The complete 'just compile' code can be found here: https://gcc.godbolt.org/z/5GMEGKG7T)
I want to have a "special" output stream, which, for certain user types, does special treatment during stream processing.
This special stream should be able to stream a "special" type with "special" treatment, as well as any other type which could be streamed through conventional streams - in the latter case, we simply use the conventional stream directly
struct Type { }; // Special type
struct Stream { // Special stream
// In this distilled example, we could achieve the same result
// by overriding << for ostream and Type, but the actual use case is wider
// and requires usage of the special stream type
// For illustration purposes only, we use this overload to cout "T!" for Types
Stream& stream(Type t) { std::cout << "T!"; return *this;}
// For any type for which cout << T{} is a valid operation, use cout for streaming
template<class T>
auto stream(const T& t) -> decltype(std::cout << std::declval<T>(), std::declval<Stream&>())
{
std::cout << t; return *this;
}
};
// Consumes every T for which Stream.stream is defined - those would be Type
// as well as anything else which can be sent to cout
template<class T>
auto operator<<(Stream& s, const T& t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
This code works as expected:
void foo()
{
Stream stream;
stream << 10 << Type{};
}
Now I want to define a different struct, which has a member of Type in it, and a streaming operator for the same struct:
struct Compound {
Type t;
int i;
};
// We want the `<<` operator of the struct templated, because we want it to work with
// any possible stream-like object out there
template<class S>
S& operator<<(S& s, Compound comp)
{
s << comp.t << " " << comp.i;
return s;
};
Unfortunately, this doesn't work well. An attempt to use this << operator:
void bar(Compound comp)
{
Stream s;
s << comp;
}
Triggers an ambiguity:
error: ambiguous overload for 'operator<<' (operand
types are 'Stream' and 'Compound')
note: candidate: 'decltype (s.Stream::stream(t)) operator<<(Stream&, const T&)
note: candidate: 'S& operator<<(S&, Compound) [with S = Stream]'
That makes sense, since after defining templated operator for << for Compound, it could be streamed via cout, as well as through this new operator!
I can certainly work aorund this by making << for Compound non-templated, and instead use the special Stream as a stream argument - but I'd like to avoid it.
Is there a way I can preserve the templated << for Compound and still avoid the ambiguity?
So you need to lower the priority of operator<<(Stream& s, const T& t) to make it the last resort.
You can do it by making it (seemingly) less specialized, by templating the first parameter:
auto operator<<(std::same_as<Stream> auto &s, const auto &t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
The change I made to the second parameter is unimportant, it's just for terseness.
Some extra boilerplate maybe?
template<class T> struct IsDefault: std::true_type {};
template<class T> concept Default = IsDefault<T>::value;
struct Stream {
template<Default T> Stream &operator<<(T &&t) {
return std::cout << t, *this;
}
};
struct Compound {};
template<> struct IsDefault<Compound>: std::false_type {};
Stream &operator<<(Stream &s, Compound &&c) { return s << "Compound"; }
I am trying to built a templated num class. This class needs to have a public attribute, val, with type T, which is the only templated parameter. Furthermore if one provides a value the attribute (val) should be initialized with this value. To do so I made the following code:
#include <iostream>
template<class T>
class Num {
public:
T val;
Num():val(0) { std::cout<<"default constr used"<<std::endl; }
Num(T value):val(value) {std::cout<<"constr (T value) used"<<std::endl; }
~Num() { std::cout<<"destructor used"<<std::endl; }
template<typename U>
Num operator+(const Num<U>& other) {
return val+other.value;
}
};
Furthermore I created the main() function to test the program, which looks like this:
int main() {
std::cout << Num<int>(1) + Num<double>(2.0);
return 0;
}
However the result of the program is now 3. Whereas I expected it to be 3.0 (of type double).
For that you will need to change the return type.
In your code:
// vvv---- Means Num<T>
Num operator+(const Num<U>& other) {
return val + other.val;
}
Indeed, inside a class template, you can type the name of the class without template arguments and it's gonna be somewhat equivalent to writing Num<T>.
Your function is always returning the type of the first operant, no matter the type of the addition itself.
What you want is to deduce that type coming from the addition:
auto operator+(const Num<U>& other) -> Num<decltype(val + other.val)> {
return val + other.val;
}
That way, it's always the right return type according to the C++ operator rules.
operator+ should be symmetric with respect to its arguments. It's better be implemented as a free function rather than a member function to make this symmetry explicit.
For example (using C++14 return type deduction):
template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
using R = decltype(std::declval<T>() + std::declval<U>());
return Num<R>{x.val + y.val};
}
std::declval<T>() is there for genericity, if T and/or U are not default constructible. If types are limited to built-in ones, like int and double, it can be replaced with T{} or T():
using R = decltype(T{} + U{});
With class template argument deduction in C++17 it can be simplified further:
template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
return Num{x.val + y.val};
}
I am playing with lambda expressions and I am using auto as input parameter.
I did try this code below
auto f2 = [](auto a){ return a;};
std::cout << f2(10) << std::endl;
std::cout << f2("hi there!") << std::endl;
With my big surprise it compiles and run ok! How is that possible?
If I am not wrong (this comes with the C++14) the operator() of the function object is template since it uses auto as input parameter.
How does it manage multiple return types? First line returns an int and the second line returns const char*.
Is the compiler creating multiple operator() behind the scenes?
As you say, the operator() of generic lambda is effectively a function template. Something similar to:
struct noname
{
template<typename T>
auto operator ()(T a) const { return a; }
};
Your separate calls instantiate two different functions.
auto operator ()(int a) const { return a; }
auto operator ()(const char* a) const { return a; }
From there, the rules of auto return type apply. In the first function, the first and only return statement returns an int, therefore int is deduced as the return type. Same for const char*
I'm writing a class that overloads the << and >> operator similar to std::istream and std::ostream. The first few functions worked as desired. However, I want to use other std::*stream classes inside my own class for formatting purposes, and this causes an interesting, I would say, 'recursive' compiler issue:
class SmartStream
{
public:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
in.m_buf << f; // This causes a 'recursive' compile call
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
The actual error is long but it starts like this:
rec_stream.cpp: In instantiation of 'F& operator<<(F&, float) [with F = std::basic_ostringstream<char>]':
rec_stream.cpp:14:18: required from 'F& operator<<(F&, float) [with F = ExtraStream]'
rec_stream.cpp:60:11: required from here
rec_stream.cpp:12:9: error: 'class std::basic_ostringstream<char>' has no member named 'type'
in.type ("f");
^
So apparently the compiler applies the same overloaded << operator to the m_buf variable of type std::ostringstream, but of course that doesn't have type() and add() functions. After reading the answer to this question that kinda makes sense, but doesn't provide a solution.
No before you say, use this in SmartStream:
class SmartStream
{
....
SmartStream &operator << (float f)
{
type("f");
m_buf.str ("");
m_buf << f; // Ambiguous overload
add( m_buf.str ());
return *this;
}
};
There are two problems. First, as noted in the code, that triggers an ambiguous overload. Second, consider the following:
class ExtraStream: public SmartStream
{
public:
template <typename F> friend F &operator << (F &in, struct tm t)
{
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour; // Ambiguous overload
in.add (in.m_buf.str ());
return in;
}
};
Indeed, I am extending SmartStream to handle custom types using the mechanism laid out in that class. Using the non-friend operator will not allow me to do this:
ExtraStream es;
struct tm t1, t2;
float f1, f2;
es << f1 << t1 << f2 << t2; // works
Because after a << (float) the return type is SmartStream which does not know how to handle struct tms.
My question:
Is there a way to convince the compiler (gcc 4.8.2) to use the 'base' << operator for std::ostringstream and not the overloaded one? I tried various casts, :: resolution operators, moving the code with in.m_buf << f; to a non-templated function in SmartStream but nothing helped.
Also, why does it only use this inside the template operator << function? Any use of << on std::ostringstream outside of that function works as expected.
I posted this on the gcc buglist since I felt this is a bug or at least an ambiguity in the C++ reference. Jonathan Wakely came up with a surprisingly simple solution:
template <typename F> friend F &operator << (F &in, float f)
{
in.type ("f");
in.m_buf.str ("");
using std::operator <<; // indicate proper operator in the next line
in.m_buf << f;
in.add (in.m_buf.str ());
return in;
}
So indeed it required a using clause, just one I wouldn't expect.
So, I think the simplest thing would be to restrict the friend functions with something like std::enable_if so that they are only valid on streams that have the correct parent class.
#include <sstream>
#include <type_traits>
class SmartStream {
public:
template <typename F>
friend typename std::enable_if<std::is_base_of<SmartStream, F>::value, F&>::type
operator << (F &in, float f) {
in.type("f");
in.m_buf.str("");
in.m_buf << f;
in.add (in.m_buf.str ());
return in;
}
protected:
std::ostringstream m_buf;
void type(const std::string &_type) {};
void add(const std::string &) {};
};
class ExtraStream: public SmartStream {
public:
template <typename F>
friend typename std::enable_if<std::is_base_of<ExtraStream, F>::value, F&>::type
operator << (F &in, struct tm t) {
in.type ("tm");
in.m_buf.str ("");
in.m_buf << t.tm_hour;
in.add (in.m_buf.str ());
return in;
}
};
int main() {
SmartStream ss;
ExtraStream es;
struct tm t;
ss << 3.14f;
es << 3.14f << t << 3.14;
// ss << t; // Fails as expected.
}
I'd like to find the max Foo and call inc() on it, which is a non-const method. Of course in finding the max, I don't want to create any copies or moves, i.e. I don't want Foo foo = std::max(foo1, foo2). I tried writing my own max, and g++ insists I return a const&.
#include <iostream>
class Foo
{
public:
Foo(int x) : x_(x) { std::cout << "const" << std::endl; }
Foo(const Foo& foo) : x_(foo.x_) { std::cout << "copy const" << std::endl; }
Foo(Foo&& foo) : x_(foo.x_) { std::cout << "move const" << std::endl; }
bool operator< (const Foo& foo) const { return x_ < foo.x_; }
bool operator> (const Foo& foo) const { return x_ > foo.x_; }
void inc() { ++x_; }
int x_;
};
/*
* Doesn't compile. Must return const T& or must accept non-const T&
*
template<typename T>
inline T& my_max(const T& f1, const T& f2)
{
return f1 > f2 ? f1 : f2;
}
*
*/
int main()
{
Foo foo1(6);
Foo foo2(7);
Foo& foo = std::max(foo1, foo2); //Doesn't compile. Must be const Foo&. But then next line fails
foo.inc();
std::cout << foo.x_ << std::endl;
return 0;
}
You have 2 issues here:
Missing const qualifier in result
It is dangerous to return reference to const reference parameter
In such case:
Foo& foo = std::max(Foo(6), Foo(7));
compiler will construct temporary objects for parameters before function call and will destroy them after function call - so you will end up with reference to garbage. Of course if you will always use existing objects it will work - but it is easy to forget about such limitations.
You could remove const from parameters which will resolve both issues and it should be ok for you as you intend to modify object anyway.
template<typename T>
T my_max(T&& f1, T&& f2) {
return std::forward<T>(f1 > f2 ? f1 : f2);
}
the above is relatively solid, and will do what you need. It does require the two parameters to have the same r/l/const ness, which std::max does not. Which is why max uses const&.
A far more complex version that finds the common reference category can be written, but it can act in surprising ways.
So not be fooled by lack of & in return value above: in your use case, the above returns a reference. If passed rvalues it returns a value.
Here is an attempt at a super_max that, if passed lvalues of the same type, returns an lvalue. If passed two different types, or an rvalue, returns a copy:
template<class A, class B>
struct max_return:std::common_type<A,B>{};
template<class A>
struct max_return<A&,A&>{
using type=A&;
};
template<class A, class B>
using max_return_t = typename max_return<A,B>::type;
template<class T, class U>
max_return_t<T,U> super_max(T&& t, U&& u) {
if (t < u)
return std::forward<U>(u);
else
return std::forward<T>(t);
}
it also only uses <, and prefers the left hand side on a tie.
live example