VC++错误,编译时顺序操作
VC++ error, compile-time sequence operations
我正在尝试为编译时积分序列实现一些功能。下面是操作or的实现:
#include <cstdint>
#include <utility>
using UInt32 = std::uint32_t;
template<UInt32... s>
using Sequence = std::integer_sequence<UInt32, s...>;
template<UInt32>
constexpr bool find([[maybe_unused]] Sequence<> a = {}) noexcept {
return false;
}
template<UInt32 x, UInt32 v, UInt32... s>
constexpr bool find([[maybe_unused]] Sequence<v, s...> a = {}) noexcept {
if constexpr (v == x) {
return true;
} else {
return find<x, s...>();
}
}
constexpr auto operator|(Sequence<>, Sequence<>) noexcept {
return Sequence<>{};
}
template<UInt32 v, UInt32... s>
constexpr auto operator|(Sequence<v, s...>, Sequence<>) noexcept {
return Sequence<v, s...>{};
}
template<UInt32... s, UInt32 x, UInt32... t>
constexpr auto operator|(Sequence<s...>, Sequence<x, t...>) noexcept {
if constexpr (find<x, s...>()) {
return Sequence<s...>{} | Sequence<t...>{};
} else {
return Sequence<s..., x>{} | Sequence<t...>{}; // error C2679
}
}
通过VC++(19.16.27026.1)我尝试了几种情况来检查程序,很多情况导致编译错误:
Error C2679 binary '|': no operator found which takes a right-hand operand of type 'std::integer_sequence<uint32_t,4,5>' (or there is no acceptable conversion)
例如:
int main() {
[[maybe_unused]] constexpr auto a = Sequence<>{} | Sequence<>{};
[[maybe_unused]] constexpr auto b = Sequence<>{} | Sequence<3, 4, 5>{};
[[maybe_unused]] constexpr auto c = Sequence<1>{} | Sequence<1, 2>{};
[[maybe_unused]] constexpr auto d = Sequence<1>{} | Sequence<3, 4, 5>{}; // VC++, error C2679
}
我尝试了其他编译器,例如 GCC 8.2.0 和 Clang 7.0.0。他们编译任何案例都没有错误。
我不明白为什么会出现这个错误。有什么想法吗?
我明白为什么会出现这个错误。
考虑以下示例:
#include <cstdint>
#include <utility>
using UInt32 = std::uint32_t;
template<UInt32... s>
using Sequence = std::integer_sequence<UInt32, s...>;
template<UInt32... s, UInt32 x, UInt32... t>
constexpr auto foo(Sequence<s...>, Sequence<x, t...>) noexcept {
return 0;
}
int main() {
[[maybe_unused]] constexpr auto a = foo(Sequence<1, 2>{}, Sequence<3, 4, 5>{});
[[maybe_unused]] constexpr auto b = foo(Sequence<1, 2, 3>{}, Sequence<4, 5>{});
}
GCC 8.2.0 和 Clang 7.0.0 compile 编译成功。但是 VC++ (VS 2017) 生成以下错误:
Error C2664: 'int func<1,2,3,4,5>(std::integer_sequence<uint32_t,1,2>,std::integer_sequence<uint32_t,3,4,5>) noexcept': cannot convert argument 1 from 'std::integer_sequence<uint32_t,1,2,3>' to 'std::integer_sequence<uint32_t,1,2>'
从错误中可以看出,由于函数模板的参数列表与模板化的重合,编译器尝试为 b
使用已经为 a
定义的函数类型,导致失败,因为最终函数参数的类型不同。另外两个编译器定义了一个新函数,新类型为 b
:
a: foo<1, 2, 3, 4, 5>(Sequence<1, 2>, Sequence<3, 4, 5>)
b: foo<1, 2, 3, 4, 5>(Sequence<1, 2, 3>, Sequence<4, 5>)
于是,在计算Sequence<1>{} | Sequence<3, 4, 5>{}
时出现了如下情况:
constexpr auto operator|<1, 2, 3, 4, 5>(Sequence<1, 2>, Sequence<3, 4, 5>) noexcept {
if constexpr (find<3, 1, 2>()) { // false
return Sequence<1, 2>{} | Sequence<3, 4, 5>{};
} else {
return Sequence<1, 2, 3>{} | Sequence<4, 5>{}; // error C2679
}
}
编译器尝试使用 operator|<1, 2, 4, 5>(Sequence<1, 2>, Sequence<3, 4, 5>)
。
现在知道了问题的本质,我能够重写解决方案,以便它可以被所有三个编译器编译:
using UInt32 = std::uint32_t;
template<UInt32... s>
struct Sequence {};
template<template<UInt32...> typename S, UInt32... s>
constexpr bool empty([[maybe_unused]] S<s...> a = {}) noexcept {
return sizeof...(s) == 0;
}
template<template<UInt32...> typename S, UInt32 x, UInt32... s>
constexpr UInt32 first([[maybe_unused]] S<x, s...> a = {}) noexcept {
return x;
}
template<template<UInt32...> typename S>
constexpr auto tail([[maybe_unused]] S<> a = {}) noexcept {
return S<>{};
}
template<template<UInt32...> typename S, UInt32 x, UInt32... s>
constexpr auto tail([[maybe_unused]] S<x, s...> a = {}) noexcept {
return S<s...>{};
}
template<UInt32 x, template<UInt32...> typename S>
constexpr bool find([[maybe_unused]] S<> a = {}) noexcept {
return false;
}
template<UInt32 x, template<UInt32...> typename S, UInt32 v, UInt32... s>
constexpr bool find([[maybe_unused]] S<v, s...> a = {}) noexcept {
if constexpr (v == x) {
return true;
} else {
return find<x>(S<s...>{});
}
}
template<UInt32... s, typename S2>
constexpr auto operator|(Sequence<s...>, S2) noexcept {
if constexpr (sizeof...(s) == 0) {
return S2{};
} else if constexpr (empty(S2{})) {
return Sequence<s...>{};
} else {
constexpr auto x = first(S2{});
if constexpr (find<x>(Sequence<s...>{})) {
return Sequence<s...>{} | tail(S2{});
} else {
return Sequence<s..., x>{} | tail(S2{});
}
}
}
我正在尝试为编译时积分序列实现一些功能。下面是操作or的实现:
#include <cstdint>
#include <utility>
using UInt32 = std::uint32_t;
template<UInt32... s>
using Sequence = std::integer_sequence<UInt32, s...>;
template<UInt32>
constexpr bool find([[maybe_unused]] Sequence<> a = {}) noexcept {
return false;
}
template<UInt32 x, UInt32 v, UInt32... s>
constexpr bool find([[maybe_unused]] Sequence<v, s...> a = {}) noexcept {
if constexpr (v == x) {
return true;
} else {
return find<x, s...>();
}
}
constexpr auto operator|(Sequence<>, Sequence<>) noexcept {
return Sequence<>{};
}
template<UInt32 v, UInt32... s>
constexpr auto operator|(Sequence<v, s...>, Sequence<>) noexcept {
return Sequence<v, s...>{};
}
template<UInt32... s, UInt32 x, UInt32... t>
constexpr auto operator|(Sequence<s...>, Sequence<x, t...>) noexcept {
if constexpr (find<x, s...>()) {
return Sequence<s...>{} | Sequence<t...>{};
} else {
return Sequence<s..., x>{} | Sequence<t...>{}; // error C2679
}
}
通过VC++(19.16.27026.1)我尝试了几种情况来检查程序,很多情况导致编译错误:
Error C2679 binary '|': no operator found which takes a right-hand operand of type 'std::integer_sequence<uint32_t,4,5>' (or there is no acceptable conversion)
例如:
int main() {
[[maybe_unused]] constexpr auto a = Sequence<>{} | Sequence<>{};
[[maybe_unused]] constexpr auto b = Sequence<>{} | Sequence<3, 4, 5>{};
[[maybe_unused]] constexpr auto c = Sequence<1>{} | Sequence<1, 2>{};
[[maybe_unused]] constexpr auto d = Sequence<1>{} | Sequence<3, 4, 5>{}; // VC++, error C2679
}
我尝试了其他编译器,例如 GCC 8.2.0 和 Clang 7.0.0。他们编译任何案例都没有错误。
我不明白为什么会出现这个错误。有什么想法吗?
我明白为什么会出现这个错误。
考虑以下示例:
#include <cstdint>
#include <utility>
using UInt32 = std::uint32_t;
template<UInt32... s>
using Sequence = std::integer_sequence<UInt32, s...>;
template<UInt32... s, UInt32 x, UInt32... t>
constexpr auto foo(Sequence<s...>, Sequence<x, t...>) noexcept {
return 0;
}
int main() {
[[maybe_unused]] constexpr auto a = foo(Sequence<1, 2>{}, Sequence<3, 4, 5>{});
[[maybe_unused]] constexpr auto b = foo(Sequence<1, 2, 3>{}, Sequence<4, 5>{});
}
GCC 8.2.0 和 Clang 7.0.0 compile 编译成功。但是 VC++ (VS 2017) 生成以下错误:
Error C2664: 'int func<1,2,3,4,5>(std::integer_sequence<uint32_t,1,2>,std::integer_sequence<uint32_t,3,4,5>) noexcept': cannot convert argument 1 from 'std::integer_sequence<uint32_t,1,2,3>' to 'std::integer_sequence<uint32_t,1,2>'
从错误中可以看出,由于函数模板的参数列表与模板化的重合,编译器尝试为 b
使用已经为 a
定义的函数类型,导致失败,因为最终函数参数的类型不同。另外两个编译器定义了一个新函数,新类型为 b
:
a: foo<1, 2, 3, 4, 5>(Sequence<1, 2>, Sequence<3, 4, 5>)
b: foo<1, 2, 3, 4, 5>(Sequence<1, 2, 3>, Sequence<4, 5>)
于是,在计算Sequence<1>{} | Sequence<3, 4, 5>{}
时出现了如下情况:
constexpr auto operator|<1, 2, 3, 4, 5>(Sequence<1, 2>, Sequence<3, 4, 5>) noexcept {
if constexpr (find<3, 1, 2>()) { // false
return Sequence<1, 2>{} | Sequence<3, 4, 5>{};
} else {
return Sequence<1, 2, 3>{} | Sequence<4, 5>{}; // error C2679
}
}
编译器尝试使用 operator|<1, 2, 4, 5>(Sequence<1, 2>, Sequence<3, 4, 5>)
。
现在知道了问题的本质,我能够重写解决方案,以便它可以被所有三个编译器编译:
using UInt32 = std::uint32_t;
template<UInt32... s>
struct Sequence {};
template<template<UInt32...> typename S, UInt32... s>
constexpr bool empty([[maybe_unused]] S<s...> a = {}) noexcept {
return sizeof...(s) == 0;
}
template<template<UInt32...> typename S, UInt32 x, UInt32... s>
constexpr UInt32 first([[maybe_unused]] S<x, s...> a = {}) noexcept {
return x;
}
template<template<UInt32...> typename S>
constexpr auto tail([[maybe_unused]] S<> a = {}) noexcept {
return S<>{};
}
template<template<UInt32...> typename S, UInt32 x, UInt32... s>
constexpr auto tail([[maybe_unused]] S<x, s...> a = {}) noexcept {
return S<s...>{};
}
template<UInt32 x, template<UInt32...> typename S>
constexpr bool find([[maybe_unused]] S<> a = {}) noexcept {
return false;
}
template<UInt32 x, template<UInt32...> typename S, UInt32 v, UInt32... s>
constexpr bool find([[maybe_unused]] S<v, s...> a = {}) noexcept {
if constexpr (v == x) {
return true;
} else {
return find<x>(S<s...>{});
}
}
template<UInt32... s, typename S2>
constexpr auto operator|(Sequence<s...>, S2) noexcept {
if constexpr (sizeof...(s) == 0) {
return S2{};
} else if constexpr (empty(S2{})) {
return Sequence<s...>{};
} else {
constexpr auto x = first(S2{});
if constexpr (find<x>(Sequence<s...>{})) {
return Sequence<s...>{} | tail(S2{});
} else {
return Sequence<s..., x>{} | tail(S2{});
}
}
}