简体   繁体   English

C ++:用于检查表达式是否编译的模板

[英]C++: template to check if expression compiles

When writing template specialization with SFINAE you often come to the point where you need to write a whole new specialization because of one small not-existing member or function. 在使用SFINAE编写模板专业化时,您经常会因为一个小的不存在的成员或函数而需要编写一个全新的特化。 I would like to pack this selection into a small statement like orElse<T a,T b> . 我想把这个选择打包成一个像orElse<T a,T b>这样的小语句。

small example: 小例子:

template<typename T> int get(T& v){
    return orElse<v.get(),0>();
}

is this possible? 这可能吗?

Yes, this is more or less possible. 是的,这或多或少是可能的。 It is known as a "member detector". 它被称为“成员探测器”。 See this wikibooks link for how to accomplish this with macros. 有关如何使用宏完成此操作,请参阅此wikibooks 链接 The actual implementation will depend on whether you are using pre- or post-C++11 and which compiler you are using. 实际的实现将取决于您使用的是C ++之前的版本还是之后的11,以及您使用的是哪种编译器。

The intent of orElse<v.get(),0>() is clear enough, but if such a thing could exist, it would have to be be one of: orElse<v.get(),0>()的意图足够清楚,但如果这样的事情可能存在,那么它必须是以下之一:

Invocation Lineup 调用阵容

orElse(v,&V::get,0)
orElse<V,&V::get>(v,0)
orElse<V,&V::get,0>(v)

where v is of type V , and the function template thus instantiated would be respectively: 其中vV类型,因此实例化的函数模板将分别为:

Function Template Lineup 功能模板阵容

template<typename T>
int orElse(T & obj, int(T::pmf*)(), int deflt);

template<typename T, int(T::*)()>
int orElse(T & obj, int deflt);

template<typename T, int(T::*)(), int Default>
int orElse(T & obj);

As you appreciate, no such a thing can exist with the effect that you want. 如你所知,没有这样的东西可以存在你想要的效果。

For any anyone who doesn't get that, the reason is simply this: None of the function invocations in the Invocation Lineup will compile if there is no such member as V::get . 对于任何没有得到它的人来说,原因很简单:如果没有像V::get这样的成员, Invocation Lineup中的函数调用都不会编译。 There's no getting round that, and the fact that the function invoked might be an instantiation of a function template in the Function Template Lineup makes no difference whatever. 没有得到回合,并且调用的函数可能是函数模板阵容中的函数模板的实例化这一事实没有任何区别。 If V::get does not exist, then any code that mentions it will not compile. 如果V::get不存在,则提及它的任何代码都不会编译。

However, you seem to have a practical goal that need not be approached in just this hopeless way. 但是,你似乎有一个实际的目标,不需要以这种无望的方式接近。 It looks as if, for a given name foo and an given type R , you want to be able to write just one function template: 对于给定名称foo和给定类型R ,您希望能够只编写一个函数模板:

template<typename T, typename ...Args>
R foo(T && obj, Args &&... args);

which will return the value of R(T::foo) , called upon obj with arguments args... , if such a member function exists, and otherwise return some default R . 它将返回R(T::foo)的值,如果存在这样的成员函数,则使用参数args...调用obj ,否则返回一些默认值R

If that's right, it can be achieved as per the following illustration: 如果这是正确的,可以按照下图说明:

#include <utility>
#include <type_traits>

namespace detail {

template<typename T>

T default_ctor()
{
    return T();
}

// SFINAE `R(T::get)` exists
template<typename T, typename R, R(Default)(), typename ...Args>
auto get_or_default(
    T && obj,
    Args &&... args) ->
    std::enable_if_t<
        std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
    >::value,R>
{
    return obj.get(std::forward<Args>(args)...);
}

// SFINAE `R(T::get)` does not exist
template<typename T, typename R, R(Default)(), typename ...Args>
R get_or_default(...)
{
    return Default();
}

} //namespace detail


// This is your universal `int get(T,Args...)`
template<typename T, typename ...Args>
int get(T && obj, Args &&... args)
{
    return detail::get_or_default<T&,int,detail::default_ctor>
        (obj,std::forward<Args>(args)...);
}

// C++14, trivially adaptable for C++11

which can be tried out with: 可以试用:

#include <iostream>

using namespace std;

struct A
{
    A(){};
    int get() {
        return 1;
    }
    int get(int i) const  {
        return i + i;
    }
};

struct B
{
    double get() {
        return 2.2;
    }
    double get(double d) {
        return d * d;
    }
};

struct C{};

int main()
{
    A const aconst;
    A a;
    B b;
    C c;
    cout << get(aconst) << endl;    // expect 0
    cout << get(a) << endl;         // expect 1 
    cout << get(b) << endl;         // expect 0
    cout << get(c) << endl;         // expect 0
    cout << get(a,1) << endl;       // expect 2
    cout << get(b,2,2) << endl;     // expect 0
    cout << get(c,3) << endl;       // expect 0
    cout << get(A(),2) << endl;     // expect 4
    cout << get(B(),2,2) << endl;   // expect 0
    cout << get(C(),3) << endl;     // expect 0
    return 0;
}

There is "compound SFINAE" in play in the complicated return type: 在复杂的返回类型中有“复合SFINAE”:

std::enable_if_t<
        std::is_same<R,decltype(obj.get(std::forward<Args>(args)...))
    >::value,R>

If T::get does not exist then decltype(obj.get(std::forward<Args>(args)...) does not compile. But if it does compile, and the return-type of T::get is something other than R , then the std::enable_if_t type specifier does not compile. Only if the member function exists and has the desired return type R can the R(T::get) exists case be instantiated. Otherwise the catch-all R(T::get) does not exist case is chosen. 如果T::get不存在那么decltype(obj.get(std::forward<Args>(args)...)不会编译。但如果它编译,并且T::get的返回类型是比其他的东西R那么std::enable_if_t类型说明符不能编译。只有在成员函数存在并具有所需的返回类型R可将R(T::get) 存在的情况下被实例化。否则,包罗万象的R(T::get) 不存在案例被选中。

Notice that get(aconst) returns 0 and not 1. That's as it should be, because the non-const overload A::get() cannot be called on a const A . 请注意, get(aconst)返回0而不是1.这是应该的,因为不能在const A上调用非const重载A::get()

You can use the same pattern for any other R foo(V & v,Args...) and existent or non-existent R(V::foo)(Args...) . 您可以对任何其他R foo(V & v,Args...)以及现有或不存在的R(V::foo)(Args...)使用相同的模式。 If R is not default-constructible, or if you want the default R that is returned when R(V::foo) does not exist to be something different from R() , then define a function detail::fallback (or whatever) that returns the desired default R and specify it instead of detail::default_ctor 如果R不是默认构造的,或者如果你想要R(V::foo)不存在时返回的默认RR()不同,那么定义一个函数detail::fallback (或者其他)返回所需的默认R并指定它而不是detail::default_ctor

How nice it would be it you could further template-paramaterize the pattern to accomodate any possible member function of T with any possible return type R . 你可以进一步模板化 - 模式化模式以适应T 任何可能的成员函数以及任何可能的返回类型R是多么好。 But the additional template parameter you would need for that would be R(T::*)(typename...) ,and its instantiating value would have to be &V::get (or whatever), and then the pattern would force you into the fatal snare of mentioning the thing whose existence is in doubt. 但是你需要的额外模板参数是R(T::*)(typename...) ,它的实例化值必须是&V::get (或者其他),然后模式会强迫你进入致命的陷阱,提到存在存在疑问的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM