2016-02-11 83 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

相关问题