使用 SFINAE 检查函数 std::to_string 是否存在类型



我想创建一个函数模板,该模板专门用于可能已应用于它们的类型std::to_string,另一个用于我已为其定义mylib::to_string的类,并让任何类型可能不会落入其他实现或专用化。

例:

// I only want this to be found if std::to_string exists for ItemType
template<typename ItemType>
void func(ItemType &i)
{
std::cout << std::to_string(i);
}
// I only want this to be found if mylib::to_string exists for ItemType
template<typename ItemType>
void func(ItemType &i)
{
std::cout << mylib::to_string(i);
}
// And finally, I'd like to be able to fall through to anything that has a << defined for streams
template<>
void func<MyClass>(MyClass &i)
{
std::cout << MySpecialConverterFunc(i);
}

我总是std::enable_if语法有问题。您将如何将其添加到第一个模板中?

对于 SFINAE,您不需要专业化,而是需要过载。无论如何,这比处理专业化(那些有怪癖)更理智。SFINAE 是一种指导重载解决的机制,而不是用于选择函数模板的显式专用化。

至于支票本身,你真的不需要enable_if。只是一点点表达SFINAE会做:

template<typename ItemType>
auto func(ItemType &i) -> decltype(std::to_string(i), void())
{
std::cout << std::to_string(i);
}
void func(MyClass &i)
{
std::cout << MySpecialConverterFunc(i);
}

在上面的代码中,decltype应用于逗号表达式。左手边是std::to_string(i)的,如果它的格式不正确,整个表达式的格式不正确,我们得到一个替换错误,使编译器丢弃该重载("不是错误"部分)。如果格式正确,则右侧是void()文字,因此decltype会按预期解析为void

当替换失败时,我们只剩下第二次重载,所以它被选中。无论如何,都会为这种情况选择它,因为如果签名相同,则在重载解析中始终支持非模板重载。但是,在其他情况下添加检查以更好地指导编译器本身并不是一个坏主意。

避免使用

SFINAE 并使用 ADL 规则编写简单代码

您可以通过利用 ADL(参数相关查找)来避免使用 SFINAE。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); 
}

在此示例中,可以使用foo::MyClass调用func,因为有一个to_string函数接受命名空间foo中的MyClass

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);
}

最新更新