2016-02-11 87 views
3

可以說我有如何檢查一個成員函數是否有const超載?

struct foo { 
    void ham() {} 
    void ham() const {} 
}; 

struct bar { 
    void ham() {} 
}; 

假設我有一個模板功能,我可以告訴給定的類型是否爲ham一個const超載?

+5

你想完成什麼?這是什麼用例? – NathanOliver

+0

@NathanOliver這是一個很長的故事,但基本的想法是擁有一個比編譯錯誤更友好的錯誤消息的自動檢查器,同時儘量減少我不得不做的單獨編譯的數量。 – Xarn

+0

好的。它看起來像一個[XY問題](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)給我,所以我想我會問。 – NathanOliver

回答

3

SFINAE一遍又一遍。這是另一個沒有指定返回類型的選項,但可以指定參數。

(對比:通過@ Jarod42的方法檢查的確切簽名,返回類型+參數,其他void_t表達SFINAE東西到現在檢查ham()只有是否可以調用)

此外,它與目前的MSVC 2015 Update 1版本(不像通常的void_t的東西)。

template<typename V, typename ... Args> 
struct is_callable_impl 
{ 
    template<typename C> static constexpr auto test(int) -> decltype(std::declval<C>().ham(std::declval<Args>() ...), bool{}) { return true; } 
    template<typename> static constexpr auto test(...) { return false; } 
    static constexpr bool value = test<V>(int{}); 
    using type = std::integral_constant<bool, value>; 
}; 

template<typename ... Args> 
using is_callable = typename is_callable_impl<Args...>::type; 

使用它作爲

struct foo 
{ 
    void ham() {} 
    void ham() const {} 
    int ham(int) const {} 
}; 

int main() 
{ 
    std::cout 
     <<is_callable<foo>::value    //true 
     <<is_callable<const foo>::value   //true 
     <<is_callable<const foo, int>::value  //true 
     <<is_callable<const foo, double>::value //also true, double is converted to int 
     <<is_callable<const foo, std::string>::value //false, can't call foo::ham(std::string) const 
     <<std::endl; 
} 

Demo on Coliru

對於 「最新」 SFINAE的東西,不過,我建議你看看boost.hana

+0

由於MSVC兼容性,我最終接受了這個答案。 – Xarn

+0

關於MSVC兼容性,還可以看看[new clang plugin](https://blogs.msdn.microsoft.com/vcblog/2015/12/04/clang-with-microsoft-codegen-in-vs -2015-更新-1 /)。太棒了。 – davidhigh

6

隨着

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)    \ 
    template <typename U>             \ 
    class traitsName              \ 
    {                  \ 
    private:                \ 
     template<typename T, T> struct helper;        \ 
     template<typename T>            \ 
     static std::uint8_t check(helper<signature, &funcName>*);   \ 
     template<typename T> static std::uint16_t check(...);    \ 
    public:                 \ 
     static                \ 
     constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \ 
    } 

DEFINE_HAS_SIGNATURE(has_ham_const, T::ham, void (T::*)() const); 

然後

static_assert(has_ham_const<foo>::value, "unexpected"); 
static_assert(!has_ham_const<bar>::value, "unexpected"); 

Demo

+0

沒有辦法沒有使用醜陋的宏? –

+1

是的,老師SFINAE檢查*確切*簽名。我有時會問自己:它是否也可以擴展到任意返回類型(不帶表達式'''void_t')? – davidhigh

+0

謝謝,這有效,但我第二個戴維高的問題。這可以擴展到允許不同的返回類型嗎? – Xarn

1

這裏沒有也不會在意返回類型宏的解決方案:

template <typename T> 
struct is_well_formed : std::true_type 
{ 
}; 

template <typename T, typename = void> 
struct has_const_ham : std::false_type 
{ 
}; 

template <typename T> 
struct has_const_ham<T, 
        typename std::enable_if<is_well_formed<decltype(
         std::declval<const T&>().ham())>::value>::type> 
    : std::true_type 
{ 
}; 


static_assert(has_const_ham<foo>::value, "oops foo"); 
static_assert(!has_const_ham<bar>::value, "oops bar"); 
3

探測器(升IKE is_detected):

template <typename...> 
using void_t = void; 

template <typename T, template <typename> class D, typename = void> 
struct detect : std::false_type {}; 

template <typename T, template <typename> class D> 
struct detect<T, D, void_t<D<T>>> : std::true_type {}; 

樣品構件驗證:

template <typename T> 
using const_ham = decltype(std::declval<const T&>().ham()); 

測試:

static_assert(detect<foo, const_ham>::value, "!"); 
static_assert(!detect<bar, const_ham>::value, "!"); 

DEMO

2

另一種選擇是模擬void_t(在C正式出現++ 17),它使用expression SFINAE來製作su無論其返回類型如何,您的功能都可在const實例上調用。

#include <iostream> 
#include <type_traits> 

struct Foo 
{ 
    void ham() const; 
    void ham(); 
}; 

struct Bar { 
    void ham() {} 
}; 

template<typename...> 
using void_t = void; 

template<typename C, typename = void> 
struct has_const_ham: std::false_type{}; 

template<typename C> // specialization, instantiated when there is ham() const 
struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> : 
    std::true_type{}; 

int main() 
{ 
    std::cout << std::boolalpha; 
    std::cout << has_const_ham<Foo>::value << std::endl; 
    std::cout << has_const_ham<Bar>::value << std::endl; 
} 

編輯

如果要強制執行的返回類型,然後從std::is_same推導出專業化,像

template<typename C> // specialization, instantiated when there is ham() const 
struct has_const_ham<C, void_t<decltype(std::declval<const C&>().ham())>> : 
    std::is_same<decltype(std::declval<const C&>().ham()), void> // return must be void 
{}; 

Live on Coliru

+0

很好的編輯技巧,+1。 – davidhigh

+0

@davidhigh謝謝!我認爲所有的答案都很好,可能OP有足夠的材料來消化;) – vsoftco

+0

很迂腐:在'std :: declval '這個引用是[unneeded](http://en.cppreference.com/w/ CPP /效用/ declval)。 – davidhigh

相關問題