2017-06-06 60 views
2

如何在不使用void_t的情況下實現C++ detection idiom?換句話說,我可以僅使用C++ 03特性來實現C++ 17 std::is_detected等嗎?C++檢測成語不帶void_t

UPD根據定義,檢測習慣用法需要C++ 11。在我的問題中,我的意思是我想實現is_detected而不是void_t。我的問題在於:別名模板中未使用的參數不能保證確保SFINAE並可以被忽略,VS VS 2013有這個缺陷;另一個嘗試(如cppreference)導致編譯器崩潰(是的,cl世界上最大的編譯器)。

UPD2我想VS 2013可以打破任何C++元編程技術(也是程序員的大腦)。

+0

爲什麼不能在C++ 03中實現'void_t'? – Justin

+0

@Justin因爲C++ 03沒有可變參數模板參數。 –

+0

@RichardHodges是的,我知道。你不能實現一個可變參數'void_t',但是你可以很容易地創建一個非可變參數。如果C++ - 03沒有可變參數模板,你不能完全創建一個可變參數'is_detected',但是你可以,例如,只爲一個參數做 – Justin

回答

2

由於問題已修改且允許使用C++ 11,因此我幾乎從cppreference發佈了一個複製粘貼,沒有任何功勞。

namespace detail { 
struct nonesuch { 
    nonesuch() = delete; 
    ~nonesuch() = delete; 
    nonesuch(nonesuch const&) = delete; 
    void operator=(nonesuch const&) = delete; 
}; 

template <class Default, class AlwaysVoid, template <class...> class Op, class... Args> 
struct detector { 
    using value_t = std::false_type; 
    using type = Default; 
}; 

template <typename... Ts> 
struct my_make_void { 
    typedef void type; 
}; 

template <typename... Ts> 
using my_void_t = typename my_make_void<Ts...>::type; 

template <class Default, template <class...> class Op, class... Args> 
struct detector<Default, my_void_t<Op<Args...>>, Op, Args...> { 
    using value_t = std::true_type; 
    using type = Op<Args...>; 
}; 

} // namespace detail 

template <template <class...> class Op, class... Args> 
using is_detected = 
    typename detail::detector<detail::nonesuch, void, Op, Args...>::value_t; 

template <class T> 
using copy_assign_t = decltype(std::declval<T&>() = std::declval<const T&>()); 

struct Meow {}; 
struct Purr { 
    void operator=(const Purr&) = delete; 
}; 

int main() { 
    cerr << is_detected<copy_assign_t, Meow>::value << endl; 
    return 0; 
} 
+0

有一個問題 - VS 2013不以任何方式支持它(我試圖從cppreference中使用這種方法)。 –

3

早在 良好 舊時代,這是我們如何做到這一點

template<typename T> 
T declval(); 

template<typename T> 
struct can_foo 
{ 
    typedef char yes; 
    struct no {char c[2];}; 

    template<typename U> 
    static yes check(int (*)[sizeof(declval<U>().foo(), 1)]); 
    template<typename> 
    static no check(...); 

    enum {value = sizeof(check<T>(0)) == sizeof(yes)}; 
}; 

struct fooer 
{ 
    void foo() {} 
}; 
struct barer 
{ 
    void bar() {} 
}; 

#include<cassert> 

int main() 
{ 
    assert(can_foo<fooer>::value); 
    assert(!can_foo<barer>::value); 
} 

Live example

訣竅是濫用sizeof儘可能。請注意0​​不同於std::declval

+0

您寫道:「請注意,declval與std :: declval不同。」 我喜歡定義靜態成員,比如'template static U make();'爲此目的 –

+1

看起來這個代碼在VS 2013上不起作用(兩個值都是真的):((( –

+0

@MichaelGaluza我選擇了一個免費的函數來模擬與當前方法的相似性,VS2013拒絕這個......壯觀 –