为什么无符号类型会使 std::variant<int64_t,uint64_t> 构造起来模棱两可?



我想简单地使用unsigned(123)123u,而不是123ul(unsigned long)(123)

#include <cstdint>
#include <variant>
using namespace std;
int main()
{
using var_t = variant<int64_t,uint64_t,double>;
var_t v1{1};
var_t v2{1ul};
var_t v3{1u};  //ERROR
//   var_t v4{unsigned(1)};  //ERROR
var_t v5{uint64_t(1)};
}

实例

编译器输出


g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:10:15: error: no matching function for call to 'std::variant<long int, long unsigned int, double>::variant(<brace-enclosed initializer list>)'
10 |    var_t v3{1u};  //ERROR
|               ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1402:2: note: candidate: 'template<long unsigned int _Np, class _Up, class ... _Args, class _Tp, class> constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, std::initializer_list<_Up>, _Args&& ...) [with long unsigned int _Np = _Np; _Up = _Up; _Args = {_Args ...}; _Tp = _Tp; <template-parameter-2-5> = <template-parameter-1-5>; _Types = {long int, long unsigned int, double}]'
1402 |  variant(in_place_index_t<_Np>, initializer_list<_Up> __il,
|  ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1402:2: note:   template argument deduction/substitution failed:
main.cpp:10:15: note:   mismatched types 'std::in_place_index_t<_Idx>' and 'unsigned int'
10 |    var_t v3{1u};  //ERROR
|               ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1391:2: note: candidate: 'template<long unsigned int _Np, class ... _Args, class _Tp, class> constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = _Np; _Args = {_Args ...}; _Tp = _Tp; <template-parameter-2-4> = <template-parameter-1-4>; _Types = {long int, long unsigned int, double}]'
1391 |  variant(in_place_index_t<_Np>, _Args&&... __args)
|  ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1391:2: note:   template argument deduction/substitution failed:
main.cpp:10:15: note:   mismatched types 'std::in_place_index_t<_Idx>' and 'unsigned int'
10 |    var_t v3{1u};  //ERROR
|               ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1381:2: note: candidate: 'template<class _Tp, class _Up, class ... _Args, class> constexpr std::variant<_Types>::variant(std::in_place_type_t<_Tp>, std::initializer_list<_Up>, _Args&& ...) [with _Tp = _Tp; _Up = _Up; _Args = {_Args ...}; <template-parameter-2-4> = <template-parameter-1-4>; _Types = {long int, long unsigned int, double}]'
1381 |  variant(in_place_type_t<_Tp>, initializer_list<_Up> __il,
|  ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1381:2: note:   template argument deduction/substitution failed:
main.cpp:10:15: note:   mismatched types 'std::in_place_type_t<_Tp>' and 'unsigned int'
10 |    var_t v3{1u};  //ERROR
|               ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1371:2: note: candidate: 'template<class _Tp, class ... _Args, class> constexpr std::variant<_Types>::variant(std::in_place_type_t<_Tp>, _Args&& ...) [with _Tp = _Tp; _Args = {_Args ...}; <template-parameter-2-3> = <template-parameter-1-3>; _Types = {long int, long unsigned int, double}]'
1371 |  variant(in_place_type_t<_Tp>, _Args&&... __args)
|  ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1371:2: note:   template argument deduction/substitution failed:
main.cpp:10:15: note:   mismatched types 'std::in_place_type_t<_Tp>' and 'unsigned int'
10 |    var_t v3{1u};  //ERROR
|               ^
In file included from main.cpp:2:
/usr/local/include/c++/10.2.0/variant:1361:2: note: candidate: 'template<class _Tp, class, class, class _Tj, class> constexpr std::variant<_Types>::variant(_Tp&&) [with _Tp = _Tp; <template-parameter-2-2> = <template-parameter-1-2>; <template-parameter-2-3> = <template-parameter-1-3>; _Tj = _Tj; <template-parameter-2-5> = <template-parameter-1-5>; _Types = {long int, long unsigned int, double}]'
1361 |  variant(_Tp&& __t)
|  ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1361:2: note:   template argument deduction/substitution failed:
/usr/local/include/c++/10.2.0/variant: In substitution of 'template<class ... _Types> template<class _Tp, class> using __accepted_type = std::variant<_Types>::__to_type<__accepted_index<_Tp> > [with _Tp = unsigned int&&; <template-parameter-2-2> = void; _Types = {long int, long unsigned int, double}]':
/usr/local/include/c++/10.2.0/variant:1357:9:   required from here
/usr/local/include/c++/10.2.0/variant:1327:8: error: no type named 'type' in 'struct std::enable_if<false, void>'
1327 |  using __accepted_type = __to_type<__accepted_index<_Tp>>;
|        ^~~~~~~~~~~~~~~
/usr/local/include/c++/10.2.0/variant:1349:7: note: candidate: 'constexpr std::variant<_Types>::variant(std::variant<_Types>&&) [with _Types = {long int, long unsigned int, double}]'
1349 |       variant(variant&&) = default;
|       ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1349:15: note:   no known conversion for argument 1 from 'unsigned int' to 'std::variant<long int, long unsigned int, double>&&'
1349 |       variant(variant&&) = default;
|               ^~~~~~~~~
/usr/local/include/c++/10.2.0/variant:1348:7: note: candidate: 'constexpr std::variant<_Types>::variant(const std::variant<_Types>&) [with _Types = {long int, long unsigned int, double}]'
1348 |       variant(const variant& __rhs) = default;
|       ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1348:30: note:   no known conversion for argument 1 from 'unsigned int' to 'const std::variant<long int, long unsigned int, double>&'
1348 |       variant(const variant& __rhs) = default;
|               ~~~~~~~~~~~~~~~^~~~~
/usr/local/include/c++/10.2.0/variant:1347:7: note: candidate: 'constexpr std::variant<_Types>::variant() [with _Types = {long int, long unsigned int, double}]'
1347 |       variant() = default;
|       ^~~~~~~
/usr/local/include/c++/10.2.0/variant:1347:7: note:   candidate expects 0 arguments, 1 provided
main.cpp:10:10: warning: unused variable 'v3' [-Wunused-variable]
10 |    var_t v3{1u};  //ERROR
|          ^~

我们为variant制定的最后一条规则是,我们只考虑转换不窄化的替代方案,并且窄化确定考虑类型,而不是值。

除其他外:

  • 浮点的整数总是在缩小
  • signed到unsigned总是在缩小
  • 如果有符号类型不能表示无符号类型的所有可能值,则unsigned到signed正在缩小

所以:

  • 对于v1,源类型为int,唯一的非窄化备选方案为int64_t,并且选择了它
  • 对于v2,源类型为unsigned long。如果sizeof(int64_t) == sizeof(unsigned long),就像许多平台的情况一样,那么唯一不窄化的替代方案是uint64_t,并选择它
  • 对于v3,源类型为unsigned int。如果int是32位,则int64_tuint64_t都是可行的选项,并且两者都不比另一个好。所以它是模棱两可的

对于这种事情,使用in_place_type来明确选择您想要的替代方案要清楚得多,而不是要求读者在他们的头脑中进行过载解决。

答案取决于平台,但在我的平台(64位linux+gcc C++(上,您的结果非常合理。请注意,在以下所有结果中,转换为double的排名低于积分晋升,因此它不参与。

// Below works, as 1 is signed, and can only be promoted to int64_t.
variant<int64_t,uint64_t,double> v1{1};
// Below works, as 1ul is exactly unsigned long (64 bit for me), and is exact match
variant<int64_t,uint64_t,double> v2{1ul};
// Below doesn't work, as 1u is unsigned int, and can be equally promoted to int64_t and uint64_t - thus ambiguity
variant<int64_t,uint64_t,double> v3{1u};  //ERROR
// Below doesn't work, for exactly the same reason as one above.
variant<int64_t,uint64_t,double> v4{unsigned(1)};  //ERROR

}

我发现了一个扩展std::variant:的优雅解决方案

#include <cstdint>
#include <variant>
using namespace std;
struct var_t : variant<int64_t,uint64_t,double> {
using variant<int64_t,uint64_t,double>::variant;
constexpr var_t(unsigned u) : variant(uint64_t(u)) {}
};
int main()
{
var_t v1{1};
var_t v2{1ul};
var_t v3{1u};  // VALID
var_t v4{unsigned(1)};  // VALID
var_t v5{uint64_t(1)};
}

实例

无需使用笨重的in_place_type。当然它很有用,但在这种情况下没有。

感谢大家的关注

相关内容

  • 没有找到相关文章

最新更新