【问题标题】:VC++ error, compile-time sequence operationsVC++ 错误,编译时序列操作
【发布时间】:2019-02-01 18:44:48
【问题描述】:

我正在尝试为编译时整数序列实现一些功能。下面是操作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。他们编译任何没有错误的案例。

我不明白为什么会发生此错误。有什么想法吗?

【问题讨论】:

  • 您应该提到,这只会在带有/std:c++17 的 VS 2017 中编译,因为它使用 C++17 功能。也就是说,即使使用/permissive-,我仍然可以看到 C2679。海报使用的是最新的 VS 2017 更新(15.9.5 或更高版本)。

标签: c++ templates visual-c++ c++17 variadic-templates


【解决方案1】:

我了解错误发生的原因。

考虑以下示例:

#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 编译成功编译它。但是 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>'

从错误中可以看出,编译器尝试让ba使用已经定义好的函数,由于函数模板的参数列表和模板化类型重合,导致失败,因为函数参数的最终类型不同。其他两个编译器定义了一个新函数,新类型为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&lt;1&gt;{} | Sequence&lt;3, 4, 5&gt;{}时出现以下情况:

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|&lt;1, 2, 4, 5&gt;(Sequence&lt;1, 2&gt;, Sequence&lt;3, 4, 5&gt;)

现在知道了问题的本质,我能够重写解决方案,以便它可以被所有三个编译器编译:

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{});
        }
    }
}

【讨论】:

    猜你喜欢
    • 2010-09-30
    • 2012-11-02
    • 2010-11-08
    • 2017-12-21
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多