mardi 2 octobre 2018

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

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.

Aucun commentaire:

Enregistrer un commentaire