有没有一个通用的方法来否定与SFINAE的decltype条件? [英] Is there a generic way to negate a decltype condition with SFINAE?

查看:167
本文介绍了有没有一个通用的方法来否定与SFINAE的decltype条件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有十几个函数或者这样有两个参数:一个泛型和一个特定的类型。例如:

 模板< class A,class B& 
void foo(A& a,B& b)
{
cout< 通用回退< endl
}

template< class A>
void foo(A& a,int& i)
{
cout< generic int< endl
}

template< class A>
void foo(A& a,string& s)
{
cout< generic str< endl
}

我想创建一个重载,每当 A 是特定struct [1]的实例。我到目前为止最好的是:

  struct mine 
{
int is_special;
};

template< class A,class B>
auto foo(A& a,B& b) - > decltype(A :: is_special,void())
{
cout< 专门后备< endl
}

我想要的结果是:

  int x; 
string y;
float z;
string generic;
矿山特别

foo(generic,x); // generic int
foo(generic,y); // generic string
foo(generic,z); // generic fallback
foo(special,x); // special fallback
foo(special,y); // special fallback
foo(special,z); // special fallback

然而,上述代码不起作用,因为对于特殊情况,是一个模糊的过载。如果 A :: is_special 不是有效的类型,是否有任何简单的方法导致这些函数只创建?理想情况下,我将注释每个函数,如:

 模板< class A,class B& 
auto foo(A& a,B& b) - > decltype(doesn_work(A :: is_special),void())
// ...



<我还要求在更一般的情况下:给定任何积极的SFINAE测试导致一个函数或类创建作为测试的结果,有没有任何方式否定该测试专门用于其他情况下?本质上,相当于 if ... else if with SFINAE。



,但是我必须将所有 foo 重命名为 foo_imp ,添加 long 参数到通用的参数, int 参数到特定的参数,然后定义一个 foo 他们(ideone代码此处)。这似乎不太理想,因为它不是那么直接,虽然任何一种方式,我必须修改所有现有的 foo 无论如何。


$注意,我不能使用类型的名称,因为它是一个嵌套模板,因此会导致一个不可推导的上下文。

解决方案


本质上,等效于 if ... else if 使用SFINAE。


您可以使用额外的参数手动控制重载解析:

  template< int I> struct rank:rank< I-1> {static_assert(I> 0,); }; 
模板<> struct rank< 0> {};

template< class A,class B>
auto foo(A& a,B& b,rank< 10>) - > / *一些SFINAE * /
{
}

模板< class A,class B&
auto foo(A& a,B& b,rank< 9>) - > / *一些其他SFINAE * /
{
}

// ...

模板< class A,class B&
void foo(A& a,B& b,rank< 0>)
{
// fallback
}

template< class A ,B类>
void foo(A& a,B& b){return foo(a,b,rank 20()); }

具有最高rank的可行过载将通过重载分辨率来选择。

您不能直接否定临时 SFINAE约束(签名中使用的此表达式必须格式良好)。你需要写一个实际的trait来检测它,然后否定结果。最简单的方法是使用 std :: experimental :: is_detected_v ,最近投入了v2的图书馆基础知识TS:

  template< class T> 
using is_special_t = decltype(T :: is_special);

template< class A,class B>
auto foo(A& a,B& b) - > std :: enable_if_t<!std :: experimental :: is_detected_v< is_special_t,A>>
{
cout<< 通用后退< endl
}

template< class A,class B>
auto foo(A& a,B& b) - > std :: enable_if_t< std :: experimental :: is_detected_v< is_special_t,A>>
{
cout<< 专门后备< endl
}


I have a dozen functions or so that take two parameters: a generic, and a specific type. E.g.:

template <class A, class B>
void foo(A& a, B& b)
{
    cout << "generic fallback" << endl;
}

template <class A>
void foo(A& a, int &i)
{
    cout << "generic int" << endl;
}

template <class A>
void foo(A& a, string& s)
{
    cout << "generic str" << endl;
}

I want to create an overload which will get called whenever A is an instance of a particular struct[1]. The best I came up with so far was:

struct mine
{
    int is_special;
};

template <class A, class B>
auto foo(A& a, B& b) -> decltype(A::is_special, void())
{
    cout << "specialized fallback" << endl;
}

The result I want is:

int x;
string y;
float z;
string generic;
mine special;

foo(generic, x); // generic int
foo(generic, y); // generic string
foo(generic, z); // generic fallback
foo(special, x); // specialized fallback
foo(special, y); // specialized fallback
foo(special, z); // specialized fallback

The above code doesn't work, however, because for the specialized cases, there is an ambiguous overload. Is there any easy way to cause those functions to only get created if A::is_special is not a valid type? Ideally I would annotate each function with something like:

template <class A, class B>
auto foo(A& a, B& b) -> decltype(doesnt_work(A::is_special), void())
// ...

I ask also in the more general case: given any "positive" SFINAE test which results in a function or class getting created as a result of the test, is there any way to negate that test specifically for use in other cases? In essence, the equivalent of if ... else if with SFINAE.

I did get this case to work, but I had to rename all foo to foo_imp, add a long parameter to the generic ones, an int parameter to the specialized one, and then define a foo that called them (ideone code here). This seems less-than-ideal in that it's not as straightforward, although either way I have to modify all the existing foos anyway.


[1] Note that I can't use the type's name because it is a nested template and would thus result in a non-deducible context.

解决方案

In essence, the equivalent of if ... else if with SFINAE.

You can manually control overload resolution with an extra parameter:

template<int I> struct rank : rank<I-1> { static_assert(I > 0, ""); };
template<> struct rank<0> {};

template <class A, class B>
auto foo(A& a, B& b, rank<10>) -> /*some SFINAE */
{
}

template <class A, class B>
auto foo(A& a, B& b, rank<9>) -> /* some other SFINAE */
{
}

// ...

template <class A, class B>
void foo(A& a, B& b, rank<0>)
{
// fallback
}

template <class A, class B>
void foo(A& a, B& b) { return foo(a, b, rank<20>()); }

The viable overload with the highest "rank" will be selected by overload resolution.

You can't directly negate an ad hoc SFINAE constraint ("this expression used in the signature must be well-formed"). You'll need to write an actual trait to detect it, and then negate the result. Simplest way to do it is probably by using std::experimental::is_detected_v, recently voted into v2 of the library fundamentals TS:

template<class T>
using is_special_t = decltype(T::is_special);

template <class A, class B>
auto foo(A& a, B& b) -> std::enable_if_t<!std::experimental::is_detected_v<is_special_t, A>>
{
    cout << "generic fallback" << endl;
}

template <class A, class B>   
auto foo(A& a, B& b) -> std::enable_if_t<std::experimental::is_detected_v<is_special_t, A>>
{
    cout << "specialized fallback" << endl;
}

这篇关于有没有一个通用的方法来否定与SFINAE的decltype条件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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