繁体   English   中英

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

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

在使用SFINAE编写模板专业化时,您经常会因为一个小的不存在的成员或函数而需要编写一个全新的特化。 我想把这个选择打包成一个像orElse<T a,T b>这样的小语句。

小例子:

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

这可能吗?

是的,这或多或少是可能的。 它被称为“成员探测器”。 有关如何使用宏完成此操作,请参阅此wikibooks 链接 实际的实现将取决于您使用的是C ++之前的版本还是之后的11,以及您使用的是哪种编译器。

orElse<v.get(),0>()的意图足够清楚,但如果这样的事情可能存在,那么它必须是以下之一:

调用阵容

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

其中vV类型,因此实例化的函数模板将分别为:

功能模板阵容

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);

如你所知,没有这样的东西可以存在你想要的效果。

对于任何没有得到它的人来说,原因很简单:如果没有像V::get这样的成员, Invocation Lineup中的函数调用都不会编译。 没有得到回合,并且调用的函数可能是函数模板阵容中的函数模板的实例化这一事实没有任何区别。 如果V::get不存在,则提及它的任何代码都不会编译。

但是,你似乎有一个实际的目标,不需要以这种无望的方式接近。 对于给定名称foo和给定类型R ,您希望能够只编写一个函数模板:

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

它将返回R(T::foo)的值,如果存在这样的成员函数,则使用参数args...调用obj ,否则返回一些默认值R

如果这是正确的,可以按照下图说明:

#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

可以试用:

#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;
}

在复杂的返回类型中有“复合SFINAE”:

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

如果T::get不存在那么decltype(obj.get(std::forward<Args>(args)...)不会编译。但如果它编译,并且T::get的返回类型是比其他的东西R那么std::enable_if_t类型说明符不能编译。只有在成员函数存在并具有所需的返回类型R可将R(T::get) 存在的情况下被实例化。否则,包罗万象的R(T::get) 不存在案例被选中。

请注意, get(aconst)返回0而不是1.这是应该的,因为不能在const A上调用非const重载A::get()

您可以对任何其他R foo(V & v,Args...)以及现有或不存在的R(V::foo)(Args...)使用相同的模式。 如果R不是默认构造的,或者如果你想要R(V::foo)不存在时返回的默认RR()不同,那么定义一个函数detail::fallback (或者其他)返回所需的默认R并指定它而不是detail::default_ctor

你可以进一步模板化 - 模式化模式以适应T 任何可能的成员函数以及任何可能的返回类型R是多么好。 但是你需要的额外模板参数是R(T::*)(typename...) ,它的实例化值必须是&V::get (或者其他),然后模式会强迫你进入致命的陷阱,提到存在存在疑问的东西。

暂无
暂无

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

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