2017-10-08 61 views
4

我目前正试图实现一个toString功能.toString()std::to_string()这取决于可用于推导型SFINAE回退时,函数不存在

到目前为止,我有这方面的工作片段的呼叫:

#include <iostream> 
#include <string> 

template <class T> 
auto toString(const T& obj) 
     -> decltype(obj.toString(), std::string()) 
{ 
    return obj.toString(); 
} 

template <class T> 
auto toString(const T& obj) 
     -> decltype(std::to_string(obj), std::string()) 
{ 
    return std::to_string(obj); 
} 

template <class T> 
auto toString(const T& obj) 
     -> decltype(std::string(obj)) 
{ 
    return std::string(obj); 
} 

class Foo{ 
public: 
    std::string toString() const { 
    return "Hello"; 
    } 
}; 

int main() 
{ 
    Foo bar; 
    std::cout << toString(bar); 
    std::cout << toString(5); 
    std::cout << toString("Hello const char*"); 
} 

现在我想插入一个static_assert当上面的那些过载没有可行时,因为对于较老的GCC版本的默认GCC错误消息没有很多信息。

如何检查.toString()std::to_string()是否可能用于T

到目前为止,我发现没有办法检查是否不是目前,只有其他的方式。我希望有人有一个想法如何解决这个问题,并感谢您的时间。

+2

'decltype( std :: to_string(obj),std :: string())' - TIL。但是你应该使第一个表达式失效。一个顽皮的用户超载的逗号运算符可能会搞砸你的SFINAE – StoryTeller

+1

@ max66 - 没有意义重复自己。而OP的版本可以在修复函数的返回类型的同时处理任何可转换为'std :: string'的东西。非常聪明。 – StoryTeller

+0

@StoryTeller - 你是对的。 – max66

回答

5

您还可以使用static_assert与自定义错误消息:

class Dummy 
{ 
public: 
    std::string toString() const;  
private: 
    Dummy() = default; 
}; 

template <typename... Ts> 
auto toString(Ts...) 
{ 
    static_assert(std::is_same<std::tuple<Ts...>, std::tuple<Dummy>>::value, "neither std::to_str nor member toString() exists"); 
    return ""; 
} 

live example

+0

谢谢,excactly我想要的。但是为什么这个选择不会被其他选项选中呢? –

+2

这一个实际上可能是不合格的... –

+0

@ W.F。以何种方式? –

4
namespace details{ 
    template<template<class...> class, class, class...> 
    struct can_apply : std::false_type{}; 

    template<template<class...> class Z, class...Ts> 
    struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type{}; 
} 
template<template<class...> class Z, class...Ts> 
using can_apply = details::can_apply<Z, void, Ts...>; 

template<class T> 
using dot_toString_r = decltype(std::declval<T>().toString()); 

template<class T> 
using can_dot_toString = can_apply<dot_toString_r, T>; 

我离开can_std_to_string练习。

如果您在标准的修订缺乏void_t

template<class...> struct voider { using type=void; }; 
template<class...Ts> using void_t = typename voider<Ts...>::type; 

甚至在早期编译器的工作原理。

8

您需要引入一个比您当前拥有的更差的重载,并将其删除。您不需要检查这两个字符串函数是否都以这种方式存在。

一种流行的方式做,这是用C风格的可变参数:

std::string toString(...) = delete; 
+0

谢谢,TIL你可以删除一个非成员函数:)。这是最简单的答案,但我更喜欢@ m.s的答案,因为在这种情况下更具表现力的自定义错误消息。 –