使用SFINAE根据类模板参数的类型更改类中调用的函数

using SFINAE to change function called in a class depending on type of class template parameter

本文关键字:类型 函数 调用 参数 SFINAE 使用      更新时间:2023-10-16

我有一个模板化的类,它可以采用标量或索引类型,我想根据它的类型调用不同版本的函数:

template <typename Ta, typename ... T>
struct MyClass {
    Ta tt; //can be either a scalar (double, complex<double>, int, etc), 
          //or an indexed type (MyClass, std::vector<double>, double[], etc)
    //....
    //I would like the following to be called when tt is a scalar type:
    auto operator[]( size_t i ) { return tt; };
    //I would like the following to be called when tt has [] overloaded:
    auto operator[]( size_t i ) { return tt[i]; };
};

有办法做到这一点吗?返回值SFINAE无效(因为此函数上没有模板参数(,基于类的SFINAE似乎无效(因为可变模板使末尾的伪模板参数无效(。还有其他想法吗?

我相信Xeo误解了Andrew所说的"标量"的含义。Xeo遵循C/C++对标量的定义(根据C++11,3.9/9(,Andrew的意思是更接近线性代数意义(向量空间底层场的元素(。例如,虽然Andrew的std::complex<double>是标量,但Xeo的情况并非如此,因为他使用std::is_scalar<std::complex<double>>来检查,当然,他得到了false

我相信,Andrew想要的是operator[](size_t i)回归:

  1. 如果tt[i]合法,则为tt[i];或

  2. 如果tt[i]是非法的,则为tt

我们可以轻而易举地淘汰掉上面的第一位候选人。通过实现一个检查调用tt[i]是否合法的特性,当表达式合法时,我们还可以SFINAE去掉第二个候选者。创建这个特性不是很简单,它依赖于过载解决方案(实际上是经典的no f(...)(。我将跳过特性的创建,而是直接在MyClass中使用相同的想法。为了避免用伪参数(重载解析技巧所需(污染operator[]()的签名,我将创建名为value()的私有方法,该方法使用伪参数。那么operator[]()将仅将呼叫委派给其中一个。

代码如下。

#include <type_traits> // for std::declval
template <typename Ta>
class MyClass {
    // This overload is SFINAEd away when tt[i] is illegal
    template <typename T = Ta, typename R = decltype(std::declval<T>()[0])>
    R
    value(size_t i, int) { return tt[i]; }
    // This one is always present but is a worse match than the other
    // one when resolving value(i, 0).
    const Ta&
    value(size_t, ...) { return tt; }
public:
    Ta tt;
    auto operator[]( size_t i ) -> decltype(this->value(0, 0)) {
        return value(i, 0);
    }
};

只需给operator[] s一个伪模板参数。

template<class U = Ta, EnableIf<std::is_scalar<U>>...>
auto operator[]( size_t i ){ return tt; };
template<class U = Ta, DisableIf<std::is_scalar<U>>...>
auto operator[]( size_t i ) { return tt[i]; };

请参阅此处了解有关这种特殊风格的SFINAE的一些解释。