本文介绍了为什么当有两个具有不同签名的函数时SFINAE导致失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图围绕这个问题,因为它是以这样的方式隐藏的它实际上在做什么。所以我重写了这样:

I was trying to wrap my head around this question here because it was written in such a way that it was hiding what it was actually doing. So I rewrote it as such:

template<typename CLASS>
struct has_begin
{
    // NOTE: sig_matches() must come before fn_exists() as it is used for its
    //       type.  Also, no function bodies are needed as they are never called.

    // This matching sig results in a return type of true_type
    template<typename A_CLASS>
    static auto
        sig_matches(void(A_CLASS::*)())
        -> std::true_type;

    // If the member function A_CLASS::begin exists and a sig_matches() function
    // exists with the required sig, then the return type is the return type of
    // sig_matches(), otherwise this function can't exist because at least one
    // the types don't exist so match against fn_exists(...).
    template <typename A_CLASS>
    static auto
        fn_exists(decltype(&A_CLASS::begin))          
        -> decltype(sig_matches<A_CLASS>(&A_CLASS::begin));

    // Member function either doesn't exist or doesn't match against a 
    // sig_matches() function.
    template<typename A_CLASS>
    static auto
        fn_exists(...)
        -> std::false_type;

    // Intermediate storage of type for clarity
    typedef decltype(fn_exists<CLASS>(nullptr)) type;

    // Storing the resulting value
    static int const value = type::value;
};

这样做后,发生了什么事情相当容易。然而,我发现了一些奇怪的东西。如果一个类被传递给这个具有2个开始签名,其中一个匹配 has_begin :: sig_matches(),它将无法匹配。

After doing that, it was fairly easy what was happening. However, I found something odd. If a class was passed to this with 2 begin signatures, one of which matched against has_begin::sig_matches(), it would fail to match against it.

#include <iostream>
#include <type_traits>
struct A
{
    void begin()
    {
        std::cout << "begin() called 1" << std::endl;
    }
};

struct B {};

struct C
{
    void begin()
    {
        std::cout << "begin() called 1" << std::endl;
    }

    void begin(float)
    {
        std::cout << "begin() called 2" << std::endl;
    }
};

template<typename T, typename...ARGs>
typename std::enable_if<!!has_begin<T>::value>::type
    call(ARGs...args)
{
    std::cout << "Found(" << has_begin<T>::value << ")" << std::endl;
    T().begin(args...);
}

template<typename T, typename...ARGs>
typename std::enable_if<!has_begin<T>::value>::type
    call(ARGs...)
{
    std::cout << "NOT Found(" << has_begin<T>::value << ")" << std::endl;
}

int main()
{
    call<A>(); // A::begin() called
    call<B>(); // B has no begin()
    call<C>(); // C::begin() is not called.
    return 0;
}

为什么无法与 C :: begin()

原因是& A_CLASS :: begin 是不明确的。正确的类如下:

The reason is that &A_CLASS::begin is ambiguous. The corrected class is as follows:

template<typename CLASS>
struct has_begin
{
    // NOTE: No function bodies are needed as they are never called.

    // If the member function A_CLASS::begin exists with the required sig,
    // then the return type is true_type otherwise this function can't
    // exist because the type cannot be deduced.
    template <typename A_CLASS>
    static auto
        fn_exists(decltype((void(A_CLASS::*)())&A_CLASS::begin))          
        -> std::true_type;

    // Member function either doesn't exist or doesn't match against the
    // required signature
    template<typename A_CLASS>
    static auto
        fn_exists(...)
        -> std::false_type;

    // Intermediate storage of type for clarity
    typedef decltype(fn_exists<CLASS>(nullptr)) type;

    // Storing the resulting value
    static int const value = type::value;
};

Yakk和dyp提出了一个好点。以下是一种方法,但使用兼容签名:

Yakk and dyp brought up a good point. Here is a way to do the same but with a compatible signature:

template<typename CLASS>
struct has_begin
{
    // NOTE: No function bodies are needed as they are never called.

    // If the member function A_CLASS::begin exists that has a compatible sig, 
    // then the return type is true_type otherwise this function can't exist
    // because the type cannot be deduced.
    template <typename A_CLASS>
    static auto
        fn_exists(decltype(std::declval<A_CLASS>().begin())*)          
        -> std::true_type;

    // Member function either doesn't exist or doesn't match against the
    // required compatible signature
    template<typename A_CLASS>
    static auto
        fn_exists(...)
        -> std::false_type;

    // Intermediate storage of type for clarity
    typedef decltype(fn_exists<CLASS>(nullptr)) type;

    // Storing the resulting value
    static int const value = type::value;
};

我发现这比Yakks回答更清洁,因为它不需要详细的命名空间和其他噪音但是YYMV。

I find this cleaner than Yakks answer as it doesn't require detail namespaces and other 'noise', but YYMV.

推荐答案

替换

// If the member function A_CLASS::begin exists and a sig_matches() function
// exists with the required sig, then the return type is the return type of
// sig_matches(), otherwise this function can't exist because at least one
// the types don't exist so match against fn_exists(...).
template <typename A_CLASS>
static auto
    fn_exists(decltype(&A_CLASS::begin))          
    -> decltype(sig_matches<A_CLASS>(&A_CLASS::begin));

// If the member function A_CLASS::begin exists and a sig_matches() function
// exists with the required sig, then the return type is the return type of
// sig_matches(), otherwise this function can't exist because at least one
// the types don't exist so match against fn_exists(...).
template <typename A_CLASS>
static auto
    fn_exists(std::nullptr_t)          
    -> decltype(sig_matches<A_CLASS>(&A_CLASS::begin));

As

decltype(&A_CLASS::begin) is ambiguous when there are overloads for `begin`.

这篇关于为什么当有两个具有不同签名的函数时SFINAE导致失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-21 13:49