问题
Triggered by a comment to this answer I would like to write (in C++11) a
template <typename T>
struct has_out_op { static const bool value = ???; }
to dis/enable a member function depending on std::cout << t;
being valid for some T t
. I came this far...
#include <iostream>
struct can_convert_to_base{}; // but does not when there is a better match
struct base {base(can_convert_to_base);};
template <typename T>
auto test(const T& t,can_convert_to_base)
-> decltype( std::cout << t);
template <typename T>
std::false_type test(const T& t,base);
template <typename T>
struct has_out_op {
static const bool value =
!std::is_same<std::false_type,
decltype( test(T(),can_convert_to_base()) )
>::value;
};
struct A{};
int main() {
std::cout << has_out_op<int>::value; // prints 1
std::cout << has_out_op<A>::value; // prints 0
}
This seems to work, but when I use it for what I was actually aiming for:
struct B {
template <typename T>
typename std::enable_if<has_out_op<T>::value,B&>::type operator<<(const T& t) {
std::cout << t;
return *this;
}
};
int main() {
B b;
b << "1";
}
I get the error
prog.cc: In instantiation of 'const bool has_out_op<char [2]>::value':
prog.cc:25:60: required by substitution of 'template<class T> typename std::enable_if<has_out_op<T>::value, B&>::type B::operator<<(const T&) [with T = char [2]]'
prog.cc:31:14: required from here
prog.cc:17:67: error: functional cast to array type 'char [2]'
decltype( test(T(),can_convert_to_base()) )
^
prog.cc: In function 'int main()':
prog.cc:31:11: error: no match for 'operator<<' (operand types are 'B' and 'const char [2]')
b << "1";
^
Then I realized that my has_out_op
requires T
to be default constructible, and since that I am turning in circles. When I have a value I can easily test
if std::cout << t;
is valid, but with the type alone I have no idea how to properly implement has_out_op
.
How to detect if there is a matching overload for std::cout << t;
given only decltype(t)
?
Note that I already know how to dis/enable B::operator<<
properly, but out of courisity I am still struggling with getting has_out_op
right.
回答1:
std::declval<T>() to the rescue:
Converts any type
T
to a reference type, making it possible to use member functions indecltype
expressions without the need to go through constructors.Note that because no definition exists for declval, it can only be used in unevaluated contexts; i
...
decltype( test(std::declval<T>(),can_convert_to_base()) )
...
Since we're already here, your solution is overly complicated. This is how I would do it:
struct B {
template <typename T, class = decltype(std::cout << std::declval<T>())>
B& operator<<(const T& t)
{
std::cout << t;
return *this;
}
};
though I would be interested if there is a simpler solution for has_out_op
template <typename T>
struct has_out_op_impl
{
template <class U, class = decltype(std::cout << std::declval<U>())>
static auto foo(U) -> std::true_type;
static auto foo(...) -> std::false_type;
using Type = decltype(foo(std::declval<T>()));
};
template <class T>
struct has_out_op : has_out_op_impl<T>::Type
{};
struct A{};
int t1()
{
static_assert(has_out_op<int>::value == true, "");
static_assert(has_out_op<A>::value == false, "");
}
来源:https://stackoverflow.com/questions/52617204/error-functional-cast-to-array-type-while-trying-to-detect-if-stdcout-t-i