有什么方法来检测一个函数是否存在,并可以在编译时使用? [英] Is there any way to detect whether a function exists and can be used at compile time?

查看:256
本文介绍了有什么方法来检测一个函数是否存在,并可以在编译时使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

编辑:我的问题的简短答案是,我有一个错误的观点,什么SFINAE可以做,它不检查函数体: does sfinae实例化一个函数体?



我有一个问题类似于此:是否可以编写一个C ++模板来检查函数的存在?



不同的是,我不仅要检查函数存在,但我也想知道它是否会真正通过SFINAE。这里是我想要完成的一个例子:

  struct A 
{
void FuncA (){std :: cout<< A :: FuncA< std :: endl; }
};

struct B
{
void FuncA(){std :: cout< B :: FuncA< std :: endl; }
void FuncB(){std :: cout<< B :: FuncB< std :: endl; }
};

template< typename T>
struct Inter
{
void FuncA(){t.FuncA() }
void FuncB(){t.FuncB(); }

T t;
};

//总是采用某种Inter< T> ;.
template< typename InterType>
struct Final
{
void CallFuncs()
{
// if(t.FuncA()存在并可以调用)
t.FuncA ();

// if(t.FuncB()存在并且可以被调用)
t.FuncB();
}

InterType t;
};

void DoEverything()
{
Final< Inter< A>> finalA;
Final< Inter< B>> finalB;

finalA.CallFuncs();
finalB.CallFuncs();请注意,在CallFuncs()中,FuncA()和FuncB()都将会被调用。总是存在,但它们可能无法编译,具体取决于Inter中使用的类型T.当我试图使用上述链接问题的答案似乎总是给我真正的,我猜测是因为它只是检查该函数存在,而不是它可以实际编译(虽然我不能排除我没有螺丝起来...)



为了有条件地调用函数我可以使用enable_if如下:

 模板< typename InterType> 
typename std :: enable_if< ! / *如何确定FuncA是否可以调用? * /> :: type TryCallFuncA(InterType& i)
{
}
template< typename InterType>
typename std :: enable_if< / *如何确定FuncA是否可以被调用? * /> :: type TryCallFuncA(InterType& i)
{
i.FuncA();
}

template< typename InterType>
typename std :: enable_if< ! / *如何判断FuncB是否可以调用? * /> :: type TryCallFuncB(InterType& i)
{
}
template< typename InterType>
typename std :: enable_if< / *如何确定FuncB是否可以被调用? * /> :: type TryCallFuncB(InterType& i)
{
i.FuncB();
}

template< typename InterType>
struct Final
{
void CallFuncs()
{
TryCallFuncA(t);
tryCallFuncB(t);
}

InterType t;
};

但我不知道是否有任何方式我可以得到一个布尔值传入enable_if。是否有任何方式我可以完成这个或者我需要回退到某种手动维护的类型特征,指示这些函数是否存在?



编辑:要添加一个重要的注释,在我的可用的C ++ 11功能集,我使用MSVC 2010.



<实际情况下,类Inter的实现实际上是不透明的,在那里我需要确定是否Inter :: FuncA / FuncB将编译所以我不能只是冒泡的子类型和检查的函数的存在他们。

解决方案

我没有时间来检查这个,但你可以添加一个专业化 Final template< typename T> struct Final<内部< T> > ;; (这也有助于确保类型始终为 Inner 。可以提取用于实例化 Inter



现在第二个问题是如何使用SFINAE来检测成员函数是否存在,我相信这不应太复杂(如果你不需要做这个泛型):

  //找出U是否有void f `member 
template< typename U>
struct has_member_f {
typedef char yes;
struct no {char _ [2];};
template< typename T ,void(T :: *)()=& T :: f>
static yes impl(T *);
static no impl(...);
$ b b enum {value = sizeof(impl(static_cast< U *>(0)))== sizeof(yes)};
};

你可能可以扩展这一点,使它更通用,但函数的名称,我不认为你可以使通用。当然,你可以将其写为生成 has_member _ ## arg 并使用& T :: arg 的宏。成员的类型可能更容易推广...



或者,因为我不认为这可以做成通用,你可以使用 has_member 直接在您的类型:提供两个 callFuncA 重载,一个模板与可选的第二个参数与您想要的签名和默认到转移呼叫的& T :: FuncA ,另一个是noop的省略号。然后 callFuncs 会调用 callFuncA callFuncB ,SFINAE将

 模板< typename T> 
struct Final< Inter< T> >
{
template< typename U,void(U :: *)()=& U :: FuncA>
void callFuncA(Inter< T> * x){
x.FuncA();
}
void callFuncA(...){}

void CallFuncs(){
callFuncA(& t); //不能传递nonPOD类型通过...
//类似TryCallFuncB(t);
}
Inter< T> t;
};


Edit: The short answer to my question is that I had a mistaken view of what SFINAE can do and it does not check the function body at all: does sfinae instantiates a function body?

I have an issue similar to this one: Is it possible to write a C++ template to check for a function's existence?

The difference is that I want to not only check if the function exists, but I also want to know if it will actually pass SFINAE. Here is an example of what I'm trying to accomplish:

struct A
{
    void FuncA() { std::cout << "A::FuncA" << std::endl; }
};

struct B
{
    void FuncA() { std::cout << "B::FuncA" << std::endl; }
    void FuncB() { std::cout << "B::FuncB" << std::endl; }
};

template<typename T>
struct Inter
{
    void FuncA() { t.FuncA(); }
    void FuncB() { t.FuncB(); }

    T t;
};

// Always takes some sort of Inter<T>.
template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        // if( t.FuncA() exists and can be called )
            t.FuncA();

        // if( t.FuncB() exists and can be called )
            t.FuncB();
    }

    InterType t;
};

void DoEverything()
{
    Final<Inter<A>> finalA;
    Final<Inter<B>> finalB;

    finalA.CallFuncs();
    finalB.CallFuncs();
}

Note that in CallFuncs(), both FuncA() and FuncB() will always exist, but they may not compile depending on the type T used in Inter. When I tried to use the answer in the above linked question it seemed to always give me true which I'm guessing is because it's only checking that the function exists, not that it can actually be compiled (though I can't rule out that I didn't screw something up...)

In order to conditionally call the functions I figure I can use enable_if as such:

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
    i.FuncA();
}

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
    i.FuncB();
}

template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        TryCallFuncA(t);
        TryCallFuncB(t);
    }

    InterType t;
};

but I'm not sure if there's any way I can get a boolean value to pass into enable_if. Is there any way I can accomplish this or do I need to fall back to some sort of manually maintained type traits that indicate whether the functions exist?

For what it's worth as far as the available C++11 feature set, I'm using MSVC 2010.

edit: To add an important note, in my actual situation the implementation of the class Inter is effectively opaque at the point where I need to determine whether or not Inter::FuncA/FuncB will compile so I can't just bubble up the child types and check for the existence of the function on them.

解决方案

I don't have the time to check this now, but you can add an specialization of Final: template <typename T> struct Final< Inner<T> >; (which also helps ensure that the type is always a Inner. With that you can extract the type used to instantiate Inter.

Now the second problem is how to use SFINAE to detect whether a member function exists. I believe this should not be too complex (if you don't need to make this generic):

// Find out whether U has `void f()` member
template <typename U>
struct has_member_f {
    typedef char yes;
    struct no { char _[2]; };
    template<typename T, void (T::*)() = &T::f>
    static yes impl( T* );
    static no  impl(...);

    enum { value = sizeof( impl( static_cast<U*>(0) ) ) == sizeof(yes) };
};

You might be able to extend this a bit to make it a bit more generic, but the name of the function I don't think you can make generic. Of course, you could write that as a macro that generates has_member_##arg and uses &T:: arg. The type of the member is probably easier to generalize...

Alternatively, since I don't think this can be made generic, you can use the trick inside has_member directly in your type: provide two callFuncA overloads, one templated with the optional second argument with the signature that you want and defaulted to &T::FuncA that forwards the call, the other with ellipsis that is a noop. Then callFuncs would call callFuncA and callFuncB, and SFINAE will dispatch to either the forwarder or the noon and you get your desired behavior.

template<typename T>
struct Final< Inter<T> >
{
    template <typename U, void (U::*)() = &U::FuncA>
    void callFuncA( Inter<T>* x ) {
        x.FuncA();
    }
    void callFuncA(...) {}

    void CallFuncs() {
        callFuncA(&t);                 // Cannot pass nonPOD types through ...
        // Similarly TryCallFuncB(t);
    }
    Inter<T> t;
};

这篇关于有什么方法来检测一个函数是否存在,并可以在编译时使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆