error: functional cast to array type while trying to detect if std::cout << t; is valid

﹥>﹥吖頭↗ 提交于 2019-12-13 10:05:45

问题


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 in decltype 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!