如何实现智能模板类型转换(例如 C<int> / C<double> -> C<double>)
how to implement smart templated type casting (e.g. C<int> / C<double> -> C<double>)
考虑以下代码:
template<typename T, typename U>
constexpr auto divide(T rhs, U lhs) { return rhs / static_cast<T>(lhs); }
此处 divide 默认为 rhs 类型(这与我们从编译器获得的默认行为相同。
但是如果我们想让 divide 更智能,它会在编译时将最佳类型推导出为 return(即不会导致任何参数变窄的类型)。
所以 divide(2, 3.0) 会得到 0.6666... divide(2.0, 3) 也是如此,但是 divide(2, 3) 仍然会得到 0.
类似
template<typename T, typename U>
constexpr auto divide2(T rhs, U lhs) {
if constexpr (is_same<U, double>::value)
return static_cast<double>(rhs) / lhs;
else
return rhs / static_cast<T>(lhs);
}
但更通用 - 我可以检查每种可能的类型,但有更好的方法吗?
是否有巧妙地使用概念或元编程方法来找出更广泛的类型并选择它作为转换类型?像 max_type(T, U)
?
我想你在找 std::common_type
:
#include <type_traits>
#include <iostream>
template<typename T>
constexpr T divide_impl(T rhs, T lhs) { return rhs /lhs; }
template<typename T, typename U>
constexpr auto divide(T rhs, U lhs) { return divide_impl<std::common_type_t<T,U>>(rhs,lhs); }
int main ()
{
std::cout << std::is_same_v<int, decltype( divide(2,2))> << "\n";
std::cout << std::is_same_v<double, decltype( divide(2.0,2))> << "\n";
}
输出是
1
1
因为 divide(2,2)
returns 一个 int
而 divide(2.0,2)
returns 一个 double
.
然而,....
Here divide defaults to the rhs type (which is the same default behavior we get from the compiler.
这是不对的。算术转换见此处:https://en.cppreference.com/w/c/language/conversion。正如评论中提到的,实际上不需要演员表。以上仅在更一般的情况下有用,但您不会为 rhs / lhs
.
做这样的事情
使用 common_type_t
和 constexpr
。
#include <type_traits>
template<typename type1_t, typename type2_t>
constexpr auto divide(const type1_t& value1, const type2_t& value2)
{
using type_t = std::common_type_t<type1_t, type2_t>;
return static_cast<type_t>(value1) / static_cast<type_t>(value2);
}
int main()
{
static_assert(0.5 == divide(1.0, 2));
static_assert(3ul == divide(10, 3ul));
static_assert(0.5f == divide(1.0, 2.0f));
}
考虑以下代码:
template<typename T, typename U>
constexpr auto divide(T rhs, U lhs) { return rhs / static_cast<T>(lhs); }
此处 divide 默认为 rhs 类型(这与我们从编译器获得的默认行为相同。
但是如果我们想让 divide 更智能,它会在编译时将最佳类型推导出为 return(即不会导致任何参数变窄的类型)。
所以 divide(2, 3.0) 会得到 0.6666... divide(2.0, 3) 也是如此,但是 divide(2, 3) 仍然会得到 0.
类似
template<typename T, typename U>
constexpr auto divide2(T rhs, U lhs) {
if constexpr (is_same<U, double>::value)
return static_cast<double>(rhs) / lhs;
else
return rhs / static_cast<T>(lhs);
}
但更通用 - 我可以检查每种可能的类型,但有更好的方法吗?
是否有巧妙地使用概念或元编程方法来找出更广泛的类型并选择它作为转换类型?像 max_type(T, U)
?
我想你在找 std::common_type
:
#include <type_traits>
#include <iostream>
template<typename T>
constexpr T divide_impl(T rhs, T lhs) { return rhs /lhs; }
template<typename T, typename U>
constexpr auto divide(T rhs, U lhs) { return divide_impl<std::common_type_t<T,U>>(rhs,lhs); }
int main ()
{
std::cout << std::is_same_v<int, decltype( divide(2,2))> << "\n";
std::cout << std::is_same_v<double, decltype( divide(2.0,2))> << "\n";
}
输出是
1
1
因为 divide(2,2)
returns 一个 int
而 divide(2.0,2)
returns 一个 double
.
然而,....
Here divide defaults to the rhs type (which is the same default behavior we get from the compiler.
这是不对的。算术转换见此处:https://en.cppreference.com/w/c/language/conversion。正如评论中提到的,实际上不需要演员表。以上仅在更一般的情况下有用,但您不会为 rhs / lhs
.
使用 common_type_t
和 constexpr
。
#include <type_traits>
template<typename type1_t, typename type2_t>
constexpr auto divide(const type1_t& value1, const type2_t& value2)
{
using type_t = std::common_type_t<type1_t, type2_t>;
return static_cast<type_t>(value1) / static_cast<type_t>(value2);
}
int main()
{
static_assert(0.5 == divide(1.0, 2));
static_assert(3ul == divide(10, 3ul));
static_assert(0.5f == divide(1.0, 2.0f));
}