模板推导与隐式用户定义转换运算符
Template deduction vs. implicit user-defined conversion operator
我尝试实现了一个涉及模板的用户定义类型转换的小例子。
#include <cassert>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <type_traits>
template <typename T>
concept bool UIntegral = requires() {
std::is_integral_v<T> && !std::is_signed_v<T>;
};
class Number
{
public:
Number(uint32_t number): _number(number)
{
if (number == 1) {
number = 0;
}
for (; number > 1; number /= 10);
if (number == 0) {
throw std::logic_error("scale must be a factor of 10");
}
}
template <UIntegral T>
operator T() const
{
return static_cast<T>(this->_number);
}
private:
uint32_t _number;
};
void changeScale(uint32_t& magnitude, Number scale)
{
//magnitude *= scale.operator uint32_t();
magnitude *= scale;
}
int main()
{
uint32_t something = 5;
changeScale(something, 100);
std::cout << something << std::endl;
return 0;
}
我得到以下编译错误(来自 GCC 7.3.0):
main.cpp: In function ‘void changeScale(uint32_t&, Number)’:
main.cpp:40:15: error: no match for ‘operator*=’ (operand types are ‘uint32_t {aka unsigned int}’ and ‘Number’)
magnitude *= scale;
注意注释掉的行 - 这行有效:
//magnitude *= scale.operator uint32_t();
为什么不能自动推导模板化转换运算符?在此先感谢您的帮助。
[编辑]
我按照删除概念的建议使用 Clang 并查看了它的错误消息。我得到以下信息(被截断但足够了):
main.cpp:34:15: error: use of overloaded operator '*=' is ambiguous (with operand types 'uint32_t'
(aka 'unsigned int') and 'Number')
magnitude *= scale;
~~~~~~~~~ ^ ~~~~~
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, float)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, double)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, long double)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, __float128)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, int)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, long long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, __int128)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned int)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned __int128)
因此,打开这些概念后,我假设转换数字的唯一方法是将其转换为 无符号整数 类型 - 那么为什么它对编译器来说不够推导出转换?
requires
概念表达式的工作方式与 SFINAE 类似,它只检查表达式是否 有效 ,但不对其求值。
要使概念实际上 限制 T
为 无符号整数类型 ,请使用 bool
表达式:
template<typename T>
concept bool UIntegral = std::is_integral_v<T> && !std::is_signed_v<T>;
这能解决您的问题吗?不幸的是,请继续阅读...
Why can't the templated conversion operator be automatically deduced?
编写错误的 C++ 代码肯定会遇到编译器错误:-) gcc 中有超过 1,000 个已确认的未解决错误。
是的,找到了模板化转换运算符 ,"no match for 'operator*='"
错误消息应该改为 "ambiguous overload for 'operator*='"
。
So, having the concepts turned on I assume that the only way to cast a Number is to do it to an unsigned integral type - then why is it insufficient for the compiler to deduce the conversion?
即使修复了概念要求和编译器错误,歧义仍然存在,特别是这四个:
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned int)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned __int128)
这是因为对于每个可以想到的提升的 built-in 类型,有 a lot 个 built-in 运算符,并且 int
、long
、long long
和 __int128
都是 整数 类型。
因此,将转换模板化为 built-in 类型通常不是一个好主意。
解决方案 1. 制作转换运算符模板 explicit
并显式请求转换
magnitude *= static_cast<uint32_t>(scale);
// or
magnitude *= static_cast<decltype(magnitude)>(scale);
解决方案 2. 只需将 non-templated 转换为 _number
的类型:
struct Number
{
using NumberType = uint32_t;
operator NumberType () const
{
return this->_number;
}
NumberType _number;
};
我尝试实现了一个涉及模板的用户定义类型转换的小例子。
#include <cassert>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <type_traits>
template <typename T>
concept bool UIntegral = requires() {
std::is_integral_v<T> && !std::is_signed_v<T>;
};
class Number
{
public:
Number(uint32_t number): _number(number)
{
if (number == 1) {
number = 0;
}
for (; number > 1; number /= 10);
if (number == 0) {
throw std::logic_error("scale must be a factor of 10");
}
}
template <UIntegral T>
operator T() const
{
return static_cast<T>(this->_number);
}
private:
uint32_t _number;
};
void changeScale(uint32_t& magnitude, Number scale)
{
//magnitude *= scale.operator uint32_t();
magnitude *= scale;
}
int main()
{
uint32_t something = 5;
changeScale(something, 100);
std::cout << something << std::endl;
return 0;
}
我得到以下编译错误(来自 GCC 7.3.0):
main.cpp: In function ‘void changeScale(uint32_t&, Number)’:
main.cpp:40:15: error: no match for ‘operator*=’ (operand types are ‘uint32_t {aka unsigned int}’ and ‘Number’)
magnitude *= scale;
注意注释掉的行 - 这行有效:
//magnitude *= scale.operator uint32_t();
为什么不能自动推导模板化转换运算符?在此先感谢您的帮助。
[编辑]
我按照删除概念的建议使用 Clang 并查看了它的错误消息。我得到以下信息(被截断但足够了):
main.cpp:34:15: error: use of overloaded operator '*=' is ambiguous (with operand types 'uint32_t'
(aka 'unsigned int') and 'Number')
magnitude *= scale;
~~~~~~~~~ ^ ~~~~~
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, float)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, double)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, long double)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, __float128)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, int)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, long long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, __int128)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned int)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned __int128)
因此,打开这些概念后,我假设转换数字的唯一方法是将其转换为 无符号整数 类型 - 那么为什么它对编译器来说不够推导出转换?
requires
概念表达式的工作方式与 SFINAE 类似,它只检查表达式是否 有效 ,但不对其求值。
要使概念实际上 限制 T
为 无符号整数类型 ,请使用 bool
表达式:
template<typename T>
concept bool UIntegral = std::is_integral_v<T> && !std::is_signed_v<T>;
这能解决您的问题吗?不幸的是,请继续阅读...
Why can't the templated conversion operator be automatically deduced?
编写错误的 C++ 代码肯定会遇到编译器错误:-) gcc 中有超过 1,000 个已确认的未解决错误。
是的,找到了模板化转换运算符 "no match for 'operator*='"
错误消息应该改为 "ambiguous overload for 'operator*='"
。
So, having the concepts turned on I assume that the only way to cast a Number is to do it to an unsigned integral type - then why is it insufficient for the compiler to deduce the conversion?
即使修复了概念要求和编译器错误,歧义仍然存在,特别是这四个:
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned int)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned long long)
main.cpp:34:15: note: built-in candidate operator*=(unsigned int &, unsigned __int128)
这是因为对于每个可以想到的提升的 built-in 类型,有 a lot 个 built-in 运算符,并且 int
、long
、long long
和 __int128
都是 整数 类型。
因此,将转换模板化为 built-in 类型通常不是一个好主意。
解决方案 1. 制作转换运算符模板 explicit
并显式请求转换
magnitude *= static_cast<uint32_t>(scale);
// or
magnitude *= static_cast<decltype(magnitude)>(scale);
解决方案 2. 只需将 non-templated 转换为 _number
的类型:
struct Number
{
using NumberType = uint32_t;
operator NumberType () const
{
return this->_number;
}
NumberType _number;
};