我正在尝试为Vector类实现基本的算术运算,并且希望支持混合基础类型,同时防止发生缩小。
template <typename T1,typename T2>
Vector<T1> operator+( Vector<T1> lhs, const Vector<T2>& rhs, std::enable_if< ! is_narrowing_conversion<T2,T1>::value >::type* = nullptr )
{ return lhs += rhs; }
我想实现is_narrowing_conversion,这样只有在类型不缩小的情况下它才会允许转换。以下是一些例子:
Vector<double> a = Vector<double>() + Vector<float>(); //OK
Vector<float> a = Vector<float> + Vector<double>; //Fails to find the operator+ function
最后,我想编写第二个模板运算符+函数,通过返回Vector来处理逆情况。
我发现了这篇文章with an incomplete example。但这是不够的,因为他指出它不允许uint8_t进行uint64_t转换。
我还发现了Daniel Krügler's paper on fixing is_constructible。特别是在本文中,他提到使用已经缩小语义的列表初始化,但是我不确定如何将他提到的内容转换为可以用于SFINAE模板推导的适当特征。
答案 0 :(得分:0)
如何通过std::common_type
使用SFINAE?
以下是一个简化示例(没有Vector
但是简单值;没有operator+()
但是sum()
函数)但我希望您能理解我的意思
#include <iostream>
#include <type_traits>
template <typename T1, typename T2>
T1 sum (T1 t1,
T2 const & t2,
typename std::enable_if<std::is_same<
T1,
typename std::common_type<T1, T2>::type
>::value>::type * = nullptr)
{ return t1 += t2; }
int main()
{
float a = 1.1f;
double b = 2.2;
long double c = 3.3;
std::cout << sum(b, a) << std::endl;
std::cout << sum(c, a) << std::endl;
std::cout << sum(c, b) << std::endl;
// std::cout << sum(a, b) << std::endl; compilation error
// std::cout << sum(a, c) << std::endl; compilation error
// std::cout << sum(b, c) << std::endl; compilation error
}
答案 1 :(得分:0)
对Vector
有一些假设,但你应该明白这个想法:
template <typename T1,typename T2>
Vector<typename std::common_type<T1, T2>::type>
operator+(Vector<T1> const& lhs, Vector<T2> const& rhs)
{
std::size_t const n = std::min(lhs.size(), rhs.size());
Vector<typename std::common_type<T1, T2>::type> res(n);
for(std::size_t i{}; i < n; ++i) res[i] = a[i] + b[i];
return res;
}
Vector<double> a = Vector<double>() + Vector<float>(); // OK
Vector<double> b = Vector<float>() + Vector<double>(); // OK
答案 2 :(得分:0)
您可以使用{}
构造函数,让它为您检测narrowing conversions
你可以这样做:
template<typename T>
struct Vector {
T value;
};
template <typename T1, typename T2>
auto operator+(Vector<T1>, const Vector<T2>& rhs)
-> decltype(T1{rhs.value}, Vector<T1>{})
{ return {}; }
int main() {
auto a = Vector<double>{} + Vector<float>{};
//auto b = Vector<float>{} + Vector<double>{};
(void)a;
}
也就是说,您可以使用通常的表格来处理涉及尾随返回类型的sfinae表达式
如果您的Vector
类模板没有默认构造函数,您仍然可以使用std::declval
处理它:
-> decltype(T1{rhs.value}, std::declval<Vector<T1>>())
请注意,由于[dcl.init.list]/7.2以及后面的其他项目符号,您不能在上面的代码中执行此操作:
-> decltype(T1{T2{}}, Vector<T1>{})
否则,在您的具体示例中,以下内容有效:
auto b = Vector<float>{} + Vector<double>{};
因此,您必须使用rhs
提供的实际值和(让我说)测试以及用于专门化lhs
的实际类型。
只要可以访问所包含的值(无论它是有效的还是运算符是您班级的朋友),这应该不是问题。
作为旁注,您无法仅使用is_narrowing_conversion
和T1
类型正确定义在您的案例中正常运作的T2
。
例如,考虑[dcl.init.list]/7.2(强调我的)和你提出的测试代码:
[...]或从double到float,,除非source是常量表达式,转换后的实际值在可以表示的值范围内(即使它不能精确表示) [...]
因为您没有用于进行测试的实际值,所以您可以尝试使用T1{T2{}}
之类的东西。无论如何这不起作用。换句话说,由于上面提到的子弹,float{double{}}
将被接受而没有错误
类似的东西适用于其他几种类型。
另一种有效的方法是使用std::common_type
无论如何,已经提出了pretty good answer。不值得重复示例代码。