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

时间:2013-03-12 01:41:18

标签: c++ templates c++11 sfinae

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

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似乎不起作用(因为可变参数模板最终使虚拟模板参数不起作用)。还有其他想法吗?

2 个答案:

答案 0 :(得分:4)

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

我相信,安德鲁想要的是operator[](size_t i)返回:

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

  2. tt如果tt[i]是非法的。

  3. 我们可以很容易地将SFINAE带走上面的第一个候选人。通过实现检查调用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);
        }
    
    };
    

答案 1 :(得分:3)

只需给operator[]一个虚拟模板参数。

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的一些解释,请参阅here