If you can ensure that at most one of reader.ReadString
or reader.ReadLine
is defined, use SFINAE to control overloading (Live at Coliru):
template <typename T>
auto ReadLine(T& reader, std::string& line) -> decltype(reader.ReadString(line)) {
return reader.ReadString(line);
}
template <typename T>
auto ReadLine(T& reader, std::string& line) -> decltype(reader.ReadLine(line)) {
return reader.ReadLine(line);
}
The inapplicable implementation will trigger SFINAE and be removed from the overload set, leaving only the correct implementation.
In C++14 you'll be able to omit the trailing return type and simply use return type deduction.
In a future revision of C++, Concepts Lite will enable this to be done more cleanly. Given concepts that discriminate the two different kinds of readers - say StringReader
and LineReader
:
template <typename T>
concept bool StringReader() {
return requires(T& reader, std::string& line) {
{reader.ReadString(line)} -> bool;
};
}
template <typename T>
concept bool LineReader() {
return requires(T& reader, std::string& line) {
{reader.ReadLine(line)} -> bool;
};
}
you'll be able to directly constrain your implementations to the set of types that model those concepts:
bool ReadLine(StringReader& reader, std::string& line) {
return reader.ReadString(line);
}
bool ReadLine(LineReader& reader, std::string& line) {
return reader.ReadLine(line);
}
Hopefully you'll be able to reuse those concepts elsewhere to justify the "new-special-better" syntax being substantially longer than the old nasty syntax. Concepts Lite also will make it possible to handle types that model both concepts by explicit disambiguation:
template <typename T>
requires StringReader<T>() && LineReader<T>()
bool ReadLine(T& reader, std::string& line) {
// Call one, or the other, or do something completely different.
}