> As you can see, during serialization, it comes pretty handy to be able to check if an object has an attribute and to query the type of this attribute. In our case, it permits us to use the serialize method if available and fall back to the more generic method str otherwise.
I would actually prefer if, during serialization, the compiler would yell at me that object C doesn't have a serialize method. For one thing, it's very likely that, while it does have a toString() method, it doesn't have the corresponding fromString() method, making the serialized output useless. For the other, in a sane architecture, if an object does not have a serialize() method, then it's either: a) because it shouldn't be serialized in the first place, whatever the reason, or b) because it should have a serialize() method, but it has not yet been implemented (and it should!)
I understand that this is "didactic" example. Does anyone know of a more real-life/relevant example where this can be used? SFINAE (Substitution Failure Is Not An Error) looks neat as a language feature but I don't really see where it's useful (in fact, it looks like a bug that was repurposed as a feature).
template<class Container> Container::value_type first(Container&& c) { return *c.begin(); }
template<class ValueType> ValueType first(ValueType&& vt) { return vt; }
// Usage: first<int>(1); // 1
This call doesn't cause a compile error even though int::value_type does not exist--this is SFINAE. To make this work properly you'd also need to use compile-time introspection to disable the second function for types that have value_type, otherwise it's ambiguous.
But why would I want to do such a thing? Not only does first() not make sense for non-container objects -- but calling x.first() when x is a non-container object is a bug 9 times out of 10 (i.e. something got unpacked when it shouldn't have, an [x] (a single-object vector) ended up x (i.e. the first and only value of that vector).
For example, in arena-allocation support for protobufs, we wanted to be able to skip a destructor call to a message object if the object used only arena-allocated storage (hence had no cleanup to do). So we added a "DestructorSkippable_" typedef to any class where this was true:
class MyObject {
private:
friend class Arena;
typedef void DestructorSkippable_;
// ...
};
And then the SFINAE use at the implementation linked below [1] can either register a destructor callback, or not, depending on the class (by providing two overrides to a function, one that takes a T::DestructorSkippable_* param and one that doesn't).The essential thing about this is that it's resolved at compile time, so there's zero overhead (not even a runtime conditional-check) in the destructor-skipped case. When optimizations are enabled, constant prop and dead-code elimination can completely remove a feature that's "disabled" by a certain type trait.
[1] https://github.com/google/protobuf/blob/master/src/google/pr...
Maybe a better example would be a project that mix different libraries with different to convention for their "to string" methods: obj.toString(), to_string(obj), obj.ToString(), obj.to_string()... Which one would you use if you simply want to ouput your object on std::cout? In that case, a type check would be really handy!