Избегайте SFINAE и пишите простой код, используя правило ADL
Вы можете избежать использования SFINAE, воспользовавшись ADL (Argument Dependent Lookup). ADL утверждает, что:
Если функция и тип объявлены в одном и том же пространстве имен, вам не нужно указывать пространство имен при вызове этой функции.
Например:
namespace foo
{
class MyClass {};
std::string to_string(MyClass const& x) {
return "[Element of MyClass]";
}
}
int main() {
foo::MyClass x;
std::cout << to_string(x); // This is legal because x is in namespace foo
}
Если вы пишете to_string
функции в вашем пространстве имен для ваших типов, то вы можете написать func
один раз в следующем выражении:
template<class T>
void func(T const& t) {
using std::to_string;
// This calls std::to_string if std::to_string is defined;
// Otherwise it uses ADL to find the correct to_string function
std::cout << to_string(t);
}
В этом примере func
можно вызвать с помощью foo::MyClass
, потому что есть функция to_string
, которая принимает MyClass
в пространстве имен foo
:
int main() {
func(10); // Prints 10;
func(3.1415); // Prints 3.1415
foo::MyClass x;
func(x); // Prints [Element of MyClass]
}
А как насчет типов в пространствах имен, которые мне не принадлежат?
В этом случае я бы порекомендовал создать дополнительное пространство имен и поместить туда функции to_string
, а затем добавить его как оператор using
:
namespace textConvert {
// Because we use std::to_string here, it automatically gets included
// When we use textConvert::to_string
using std::to_string;
// Convert a vector
template<class T>
std::string to_string(std::vector<T> const& vect) {
using std::to_string;
std::string str = "[";
for(auto& elem : vect) {
str += to_string(elem);
str += ',';
}
str.back() = ']';
return str;
}
}
Затем мы можем обновить func
, чтобы включить textConvert::to_string
, и поскольку textConvert
использует std::to_string
в дополнение к различным настраиваемым функциям преобразования, оба включаются!
template<class T>
void func(T const& t) {
// This *also* automatically includes std::to_string
// Because we put using std::to_string inside textConvert
using textConvert::to_string;
std::cout << to_string(t);
}