我有一个可以采用标量或索引类型的模板类,我想根据它的类型调用函数的不同版本:
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似乎不起作用(因为可变参数模板最终使虚拟模板参数不起作用)。还有其他想法吗?
答案 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]
合法,则 tt[i]
;或
tt
如果tt[i]
是非法的。
我们可以很容易地将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。