17

Suppose you have a family of type-unrelated classes implementing a common concept by means of a given method returning a value:

class A { public: int val() const { ... } };
class B { public: int val() const { ... } };

suppose you need a generic free function taking a T returning a conventional value for whatever type NOT implementing the val method or calling the val method for ALL the types that has one:

template<class T> int val_of(const T& t) { return 0; }
template<class T> int val_of(const T& t) { return t.val(); }

Consider that A and B are just samples: you don't know how many types will ever exist implementing val, and how many types will exist not implementing it (hence explicit specialization won't scale).

Is there a simple way, based on the C++ standards, to come to a way to statically select the val_of version?

I was thinking to a std::conditional or std::enable_if, but I didn't find a simple way to express the condition.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63

4 Answers4

16

You can use return-type SFINAE:

template<typename T>
auto val_of(const T& t) -> decltype(std::declval<T>().val())
{
    return t.val();
}

int val_of(...)
{
    return 0;
}
David G
  • 94,763
  • 41
  • 167
  • 253
5

Just a somewhat longer comment... Your question has been answered. But I recently had a similar problem. Say you want to write a method to print strings to cout: Use member function write(std::cout), if not available use free function to_string(), if not available fallback to operator<<. You can use expression SFINAE as in the answer and a little class hierarchy to disambiguate the overloads:

struct S3 {};
struct S2 : S3 {};
struct S1 : S2 {};

template <class T>
auto print(S1, T const& t) -> decltype(t.write(std::cout)) {
    t.write(std::cout);
}

template <class T>
auto print(S2, T const& t) -> decltype(std::cout << to_string(t)) {
    std::cout << to_string(t);
}

template <class T>
void print(S3, T const& t) {
    std::cout << t;
}

template <class T>
void print(T const& t) {
    print(S1(), t);
}
  • Wow... interesting... that's something to really investigate on! – Emilio Garavaglia Jan 30 '14 at 07:21
  • Great trick. If you're interested, you could generalise it and write an answer for this question: [Is object slicing ever useful?](http://stackoverflow.com/questions/16416164/is-object-slicing-ever-useful) – jrok Jan 30 '14 at 13:28
  • It's not my trick... :( IIRC, I learned this from `std::advance`. I have just added this as an answer to your question. – user3250917 Feb 22 '14 at 08:25
3

Use this type trait class to determine whether a type has got val member function.

template<typename U>
struct has_val {
    typedef char yes;
    typedef struct { char c[2]; } no;

    template<typename X>
    static constexpr auto test(int) -> decltype( std::declval<X>().val(), yes() );

    template<typename X>
    static constexpr no test(...);

    static constexpr bool value = sizeof(test<U>(0)) == sizeof(yes);
};

You can use it as a condition to enable_if.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • Is there a way (probably only with the help of compiler macros) to generalise this to a `has_member_function` (i.e. `has_member_function` or something similar)? – elemakil Jan 29 '14 at 21:25
  • @elemakil You can do that with plain old macros only, I'm afraid. – jrok Jan 30 '14 at 13:24
1

The currently highest voted answer invokes undefined behavior in some cases, so I will give an alternative answer.

We start with some boilerplate machinery:

template<typename T> struct type_sink { typedef void type; }
template<typename T> using TypeSink = typename type_sink<T>::type;

Then a has_val traits class:

template<typename T, typename=void>
struct has_val : std::false_type;
template<typename T>
struct has_val<T, TypeSink< decltype(std::declval<T>().val()) > > : std::true_type;

We can then use tag dispatching to solve your problem:

template<typename T>
int val_of_internal( T const& t, std::true_type /* has_val */ ) {
  return t.val();
}
template<typename T>
int val_of_internal( T const& t, std::false_type /* has_val */ ) {
  return 0;
}
template<typename T>
int val_of( T const& t ) {
  return val_of_internal( t, has_val<T const>() );
}

If you find writing has_val tiresome and prefer macros, here is a set of macros that write the has_val for you:

#define EXPRESSION_IS_VALID_FOR_TYPE_T_TRAIT( TRAIT_NAME, ... ) \
template<typename T, typename=void> \
struct TRAIT_NAME : std::false_type {}; \
template<typename T> \
struct TRAIT_NAME< T, TypeSink< decltype( __VA_ARGS__ ) > >: std::true_type {}

#define HAS_NULLARY_METHOD_TRAIT(TRAIT_NAME, METHOD_NAME) \
EXPRESSION_IS_VALID_FOR_TYPE_T_TRAIT( TRAIT_NAME, std::declval<T>().METHOD_NAME() )

Now we can write this:

HAS_NULLARY_METHOD_TRAIT( has_val, val );

but I do not know if it is worth it.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524