c++ - 有没有一种通用的方法可以用 SFINAE 否定 decltype 条件?

标签 c++ templates c++11 generics sfinae

我有十几个函数接受两个参数:一个通用的和一个特定的类型。例如:

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

我想创建一个重载,只要 A 是特定结构 [1] 的实例,它就会被调用。到目前为止我想到的最好的是:

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

我想要的结果是:

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

但是,上面的代码不起作用,因为对于特殊情况,存在不明确的重载。有什么简单的方法可以使这些函数仅在 A::is_special 不是有效类型时创建?理想情况下,我会用类似的东西来注释每个函数:

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

我也在更一般的情况下问:给定任何导致函数或类作为测试结果创建的“正面”SFINAE 测试,是否有任何方法可以否定该测试专门用于其他情况?本质上,等同于 SFINAE 的 if ... else if

我确实让这个案例起作用了,但我不得不将所有的 foo 重命名为 foo_imp,为通用参数添加一个 long 参数,一个 int 参数到专门的参数,然后定义一个 foo 调用它们(ideone 代码 here )。这似乎不太理想,因为它不是那么简单,尽管无论如何我都必须修改所有现有的 foo


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

最佳答案

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

您可以使用额外参数手动控制重载决议:

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

具有最高“排名”的可行重载将由重载决议选择。

您不能直接否定 ad hoc SFINAE 约束(“签名中使用的表达式必须格式正确”)。您需要编写一个实际特征来检测它,然后否定结果。最简单的方法可能是使用 std::experimental::is_detected_v ,最近投票支持库基础知识 TS 的 v2:

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

关于c++ - 有没有一种通用的方法可以用 SFINAE 否定 decltype 条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30766965/

相关文章:

delphi - C++ 模板与 VCL 类可以很好地配合吗?

javascript - mustache 模式下划线模板中的循环

c++ - 根据值的概率从 Map 中获取随机键

c++ - 如何在bitset上完成C++11编译时类静态成员初始化?

c++ - 如何缩短内部类私有(private)的返回类型中的嵌套命名空间?

c++ - 如何在不将库源添加到项目的情况下将库链接到 CodeBlocks & GCC 中的项目

c++ - 替换字符串中的字符需要成本吗?

c++ - 尝试使用参数包绑定(bind) std::function 时获取未声明的参数

c++ - QML TableView 使用 QtQuick.Controls 2 单击行

c++ - 可变参数模板函数的特化