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

时间:2019-05-29 05:43:51

标签: c++ templates sfinae

我想创建一个功能模板,专门针对可能已应用std::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);
}

template<>
void func<MyClass>(MyClass &i)
{
     std::cout << MySpecialConverterFunc(i);
}

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

2 个答案:

答案 0 :(得分:5)

对于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

当替换失败时,剩下的就是第二个重载,因此将其选中。无论如何,都应该选择这种情况,因为如果签名相同,则非模板重载总是在重载解析中受到青睐。但是添加检查以更好地指导其他情况下的编译器本身并不是一个坏主意。

答案 1 :(得分:1)

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

在此示例中,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);
}