隐式转换的优先级高于透明lazyness的模板构造函数和赋值运算符



我愿意实现lazyness以避免一些昂贵的计算,并将计算延迟到需要结果的地步。为了以透明的方式做到这一点,我有这样的想法:将计算封装在懒惰类的转换运算符中:懒惰函数返回懒惰类,将懒惰类转换为结果标量类型触发计算。这基本上是有效的,但我在尝试使用区间算法(boost::numeric::interval(进行稳健计算时遇到了一个问题。在这种情况下,进行计算的interval<double>类型定义了通用模板构造函数和赋值运算符,这会导致编译时的麻烦,因为通用模板优先于隐式转换,尽管模板无法编译:其原型是有效的。以下是一个最小的问题示例:

#include <iostream>
//some generic costly operation that I'd like to avoid
template<typename T>
T complicated_stuff(const T& t) {
std::cout << "computing" << std::endl ;
return t*t ;
}
//implementing a lazy version through a convertible return type
template<typename T>
struct Lazy {
Lazy(const T& t_in) : t(t_in) {}
//the computation only happens when converted
operator T() const {
t = complicated_stuff(t) ;
return t ;
}

mutable T t ;
} ;
//generically wrapping the lazy implementation
template<typename T>
Lazy<T> lazy_complicated_stuff(const T& t) {
return Lazy<T>(t) ;
}
//the desired object type to run the computation on
//I can't modify CustomScalar, boost::numeric::interval in my use case
struct CustomScalar {
CustomScalar() {}

CustomScalar(const CustomScalar&) = default ;
//this generates my construction trouble
template<typename T>
CustomScalar(const T& v) : val(v) {}
CustomScalar& operator=(const CustomScalar&) = default ;
//this generates my assignment problems
template<typename T>
CustomScalar& operator=(const T& t) { val = t ; return *this ; }
//for the complicated stuff to work
CustomScalar operator*(const CustomScalar& rhs) const { 
return CustomScalar(val*rhs.val) ; 
}

float val ;
} ;
std::ostream& operator<<(std::ostream& out, CustomScalar s) {
out << s.val ;
return out ;
}
//some function that requires the value and therefore the computation
template<typename T>
void print(const T& t) {
std::cout << "printing" << std::endl ;
std::cout << t << std::endl ;
}
int main()
{
//some initial custom scalar
CustomScalar s0(10) ;
print(s0) ;
//ground truth computation
auto s1 = complicated_stuff(s0) ;
print(s1) ;
//lazy version computed during print
auto s2 = lazy_complicated_stuff(s0) ;
print(s2) ;
//reassigning s2
s2 = lazy_complicated_stuff(s1) ;
print(s2) ;
//now trying to reassign s1 with some lazy stuff
//desired syntax, doesn't work unless no tempate operator=
//s1 = lazy_complicated_stuff(s1) ;

//clumsy, not working unless no template constructor
//s1 = CustomScalar(lazy_complicated_stuff(s1)) ;

//unsatisfactorily working
//because both possible constructors require implicit conversion ?
//how is the priority defined then ? The non template overload ?
CustomScalar tmp = lazy_complicated_stuff(s1) ;
s1 = tmp ;
print(s1) ;

return 0 ;
}

我不知道为什么最终不令人满意的工作版本会起作用,而且它也不令人满意,因为我不能一般地使用我的懒惰类型来代替原始标量类型:使用I的代码必须经过精心编制,以解决分配和构造问题。

我没有一个优雅的解决方案来解决这个问题。目前,我的最佳选择似乎是将boost::numeric::interval类型包装在一些自定义对象中,以SFINAE删除通用模板构造函数,但我很好奇是否能找到更优雅的解决方案。

问题源于s1的类型。主要:

//...
CustomScalar s0(10) ;
//...    
auto s1 = complicated_stuff(s0) ;
// this resolves to:
CustomScalar s1 = complicated_stuff(s0) ;

一个简单的解决方案是为CustomScalar类定义complexd_stuff((和lazy_completed_stuff((的特定重载,如下所示:

inline CustomScalar complicated_stuff(const CustomScalar& s) 
{
return complicated_stuff(s.val); // call the template function
}
inline CustomScalar lazy_complicated_stuff(const CustomScalar& s) 
{
return lazy_complicated_stuff(s.val); // call the template function
}

在这里,它们返回一个CustomScalar,但如果需要的话,这些重载可以返回一个纯浮点。

这应该以非侵入性的方式解决模板化构造函数和赋值运算符的问题。

这里有一个可以玩代码的沙盒:https://godbolt.org/z/K448jfEbv

最新更新