Reputation: 2660
I appreciated the answers to this stackoverflow question Generic implementation of operator<< function in terms of the dump member function
However, the answer by Richard Hodges suggests that C++17's is_invocable() can be used to simplify this solution.
When I try to implement a solution using is_invocable(), I get a compile error.
I have an #ifdef 0 around the code that works without the is_invocable
call. That works.
How do I need to change my code to get the is_invocable call to work?
Thank you!
Compile error:
main.cpp:16:46: error: reference to non-static member function must be called
static_assert(std::is_invocable<decltype(t.dump),
~~^~~~
main.cpp:47:13: note: in instantiation of function template specialization 'operator<<<Foobar, char>' requested here
std::cout << foobar << std::endl;
^
main.cpp:32:32: note: possible target for call
std::basic_ostream<charT> &dump(std::basic_ostream<charT> &o) const
Code:
#include <iostream>
#include <string>
#include <type_traits>
template<typename T, typename charT>
auto operator<<(std::basic_ostream<charT> &str, const T &t) -> decltype(t
.dump(str))
{
#if 0
static_assert(std::is_same
<decltype(t.dump(str)),
std::basic_ostream<charT> &>::value,
".dump(ostream&) does not return ostream& !");
#else
static_assert(std::is_invocable<decltype(t.dump),
std::basic_ostream<charT> &>::value,
".dump(ostream&) does not return ostream& !");
#endif
return t.dump(str);
}
class Foobar {
public:
Foobar(std::string nameArg)
:
name(nameArg)
{}
template<typename charT>
std::basic_ostream<charT> &dump(std::basic_ostream<charT> &o) const
{
return (o << name);
}
private:
std::string name;
};
int main()
{
Foobar foobar("private name");
std::cout << foobar << std::endl;
return 0;
}
Based on the answer by @Oktalist, I have modified my code as follows. I leave the original code so the question makes sense. The modified code does not compile. I am sure I am just missing a nuance of how this works.
The compile error is
error: invalid operands to binary expression ('std::__1::ostream' (aka 'basic_ostream<char>') and 'Foobar')
std::cout << foobar << std::endl;
Here is the updated code that does not compile:
#include <iostream>
#include <string>
#include <type_traits>
template<typename T, typename charT>
auto operator<<(std::basic_ostream<charT> &str, const T &t) ->
decltype(t.dump)
{
static_assert(std::is_invocable_r<
std::basic_ostream<charT> &, // return type
decltype(&T::dump), // invocable type
T const &, // first arg type (implicit this)
std::basic_ostream<charT> & // second arg type
>::value, "dump(ostream&) does not return ostream& !");
return t.dump(str);
}
class Foobar {
public:
Foobar(std::string nameArg)
:
name(nameArg)
{}
template<typename charT>
std::basic_ostream<charT> &dump(std::basic_ostream<charT> &o) const
{
return (o << name);
}
private:
std::string name;
};
int main()
{
Foobar foobar("private name");
std::cout << foobar << std::endl;
return 0;
}
Upvotes: 0
Views: 772
Reputation: 14714
First of all, decltype(t.dump)
is invalid. You want decltype(&T::dump)
.
Secondly, you need std::is_invocable_r
if you want to check the return type, and you need to supply both argument types too:
static_assert(std::is_invocable_r<
std::basic_ostream<charT> &, // return type
decltype(&T::dump), // invocable type
T const &, // first arg type (implicit this)
std::basic_ostream<charT> & // second arg type
>::value, ".dump(ostream&) does not return ostream& !");
The above won't work as-is in your case, as dump
is a function template.
std::is_invocable_r<
std::basic_ostream<charT> &,
decltype(&T::template dump<charT>),
T const &,
std::basic_ostream<charT> &
>::value
That would work, but you are limiting yourself to a certain signature for dump
and I'm left wondering why you think it is helpful to use is_invocable
here at all.
Upvotes: 1