Is sizeof(E) an idiomatic SFINAE technique to check if E is a valid expression?
Asked Answered
G

3

14

I just found out how to check if operator<< is provided for a type.

template<class T> T& lvalue_of_type();
template<class T> T  rvalue_of_type();

template<class T>
struct is_printable
{
    template<class U> static char test(char(*)[sizeof(
        lvalue_of_type<std::ostream>() << rvalue_of_type<U>()
    )]);
    template<class U> static long test(...);

    enum { value = 1 == sizeof test<T>(0) };
    typedef boost::integral_constant<bool, value> type;
};

Is this trick well-known, or have I just won the metaprogramming Nobel prize? ;)

EDIT: I made the code simpler to understand and easier to adapt with two global function template declarations lvalue_of_type and rvalue_of_type.

Graybill answered 24/1, 2010 at 16:11 Comment(4)
With VC++ it appears that is_printable<X>::value is true for any X, and with Comeau Online it appears to be false for any X.Earthlight
With g++, I get 1 for is_printable<int>::value and 0 for is_printable<std::vector<int> >::value, so it works fine for me.Graybill
So it works on 1 out of 3 compilers...Earthlight
That's shorter: template <typename X, typename Y> static auto check(X& x, Y& y) -> decltype(x << y);Trappist
G
7

It's a well known technique, I'm afraid :-)

The use of a function call in the sizeof operator instructs the compiler to perform argument deduction and function matching, at compile-time, of course. Also, with a template function, the compiler also instantiates a concrete function from a template. However, this expression does does not cause a function call to be generated. It's well described in SFINAE Sono Buoni PDF.

Check other C++ SFINAE examples.

Gavage answered 24/1, 2010 at 16:18 Comment(0)
C
1

It is just a combination of two well-known tricks. SFINAE says 'substitution failure is not an error' - that's exactly what you did. Using sizeof to let the compiler substitute template arguments into an expression without actually executing it is also common.

Sorry :-)

Crown answered 24/1, 2010 at 16:14 Comment(0)
B
0

It used to be an idiomatic technique, but it's been completely obsoleted by newer language versions.

std::declval (C++11)

template<class T> T& lvalue_of_type();
template<class T> T  rvalue_of_type();

has been superseded by std::declval<T&>() for lvalues, and std::declval<T>() for xvalues. It's worth noting that std::declval never returns a value, in part because this would require a non-private destructor.

requires expressions and concepts (C++20)

This technique has also been obsoleted. To check whether the expression ...

stream << object; // where stream is std::ostream&, and object is T&

... is valid, you could write a concept:

template <typename T>
concept printable = requires (std::ostream& stream, T& object) {
    stream << object;
    { stream << object; } -> std::same_as<std::ostream&>;
};
Birkle answered 5/9, 2023 at 9:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.