2017-10-08 102 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的答案,因爲在這種情況下更具表現力的自定義錯誤消息。 –

相關問題