8

我试图实现一个涉及模板的用户定义类型转换的小例子。

#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: 在函数'void changeScale(uint32_t&, Number)'中:

main.cpp:40:15:错误:'operator*=' 不匹配(操作数类型为'uint32_t {aka unsigned int}'和'Number')

幅度 *= 规模;

请注意注释掉的行 - 这行有效:

//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)

因此,打开这些概念后,我假设强制转换 Number 的唯一方法是将其转换为无符号整数类型 - 那么为什么编译器不足以推断转换呢?

4

1 回答 1

1

概念表达式的requires工作方式与 SFINAE 类似,它只检查表达式是否有效,但不评估它。

要将概念实际限制 T无符号整数类型,请使用bool表达式:

template<typename T>
concept bool UIntegral = std::is_integral_v<T> && !std::is_signed_v<T>;

那会解决你的问题吗?不幸的是,没有,请继续阅读...

为什么不能自动推导出模板化的转换算子?

编写错误的 C++ 代码是解决编译器错误的可靠方法 :-) 在 gcc 中有超过 1,000 个已确认的未解决错误。

是的,应该找到模板化转换运算符,而"no match for 'operator*='"错误消息应该是"ambiguous overload for 'operator*='".

因此,打开这些概念后,我假设强制转换 Number 的唯一方法是将其转换为无符号整数类型 - 那么为什么编译器不足以推断转换?

即使概念要求和编译器错误被修复,歧义仍然存在,特别是这四个:

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)

这是因为对于每个可以想象的提升的内置类型,都有很多int内置运算符,and , long, long longand__int128都是整数类型。

因此,将转换模板化为内置类型通常不是一个好主意。

解决方案1.制作转换运算符模板explicit并显式请求转换

    magnitude *= static_cast<uint32_t>(scale);
    // or
    magnitude *= static_cast<decltype(magnitude)>(scale);

解决方案 2.只需对 的类型进行非模板转换_number

struct Number
{
    using NumberType = uint32_t;
    operator NumberType () const
    {
        return this->_number;
    }
    NumberType _number;
};
于 2018-04-06T12:39:37.107 回答