【问题标题】:Why is this a non-constant condition for g++8?为什么这是 g++8 的非常量条件?
【发布时间】:2019-06-01 17:48:43
【问题描述】:

我有这个代码。这是一个我可以在编译时使用的数组(有点像std :: array)。在编译时也是for循环。

#include <utility>
#include <memory>
#include <type_traits>

template<class F,
         std::size_t ... Is>
constexpr void __loop(F&& func, std::index_sequence<Is ...>) noexcept
{
    (func(std::integral_constant<std::size_t, Is>{}), ...);
}

template<std::size_t N,
         typename F>
constexpr void CONSTEXPR_LOOP(F&& func) noexcept
{
    __loop(std::forward<F>(func), std::make_index_sequence<N>());
}

template<typename T, std::size_t Size>
class StaticArray
{
    static_assert(std::disjunction_v<
                        std::is_default_constructible<T>,
                        std::is_nothrow_default_constructible<T>
                    >,
                  "Type must have a trivial constructor.");
public:
    constexpr StaticArray() noexcept;
    template<typename ... Args,
             std::enable_if_t<
                 std::conjunction_v<
                     std::is_same<T, Args>...
                     >
                 > * = nullptr
             >
    constexpr StaticArray(Args && ... list) noexcept;
    constexpr StaticArray(const StaticArray& a) = delete;
    constexpr StaticArray(StaticArray&& a) = delete;
    ~StaticArray() noexcept = default;

    constexpr StaticArray& operator=(const StaticArray& a) = delete;
    constexpr StaticArray& operator=(StaticArray&& a) = delete;

    constexpr const T& operator[](std::size_t i) const noexcept;
private:
    T _data[Size];
    std::size_t _capacity;
    std::size_t _count;

    template<typename Arg>
    constexpr void set_data(std::size_t i, Arg&& arg) noexcept;
    template<typename ... Args, std::size_t ... Indices>
    constexpr void unpack(std::index_sequence<Indices ...>, Args&& ... args) noexcept;
    template<typename ... Args>
    constexpr void create_indexes(Args&& ... args) noexcept;
};


template<typename T, std::size_t Size>
constexpr StaticArray<T, Size>::StaticArray() noexcept :
    _data{T{}},
    _capacity{Size},
    _count{0}
{
}

template<typename T, std::size_t Size>
template<typename ... Args,
         std::enable_if_t<
             std::conjunction_v<
                 std::is_same<T, Args>...
                 >
             > *
         >
constexpr StaticArray<T, Size>::StaticArray(Args&& ... list) noexcept :
    _data{T{}},
    _capacity{Size},
    _count{Size}
{
    static_assert(Size == sizeof ... (list), "Size of array not equal number of elements in the list");
    static_assert(std::conjunction_v<std::is_same<T, Args>... >, "Parameter must be the same type as StaticArray<T>.");
    create_indexes(std::forward<Args>(list) ...);
}

template<typename T, std::size_t Size>
template<typename Arg>
constexpr void StaticArray<T, Size>::set_data(std::size_t i, Arg&& arg) noexcept
{
    _data[i] = arg;
}

template<typename T, std::size_t Size>
template<typename ... Args, std::size_t ... Indices>
constexpr void StaticArray<T, Size>::unpack(std::index_sequence<Indices ...>, Args&& ... args) noexcept
{
    (set_data(Indices, args), ...);
}

template<typename T, std::size_t Size>
template<typename ... Args>
constexpr void StaticArray<T, Size>::create_indexes(Args&& ... args) noexcept
{
    unpack(std::make_index_sequence<Size>{}, std::forward<Args>(args)...);
}

template<typename T, std::size_t Size>
constexpr const T& StaticArray<T, Size>::operator[](std::size_t i) const noexcept
{
    return _data[i];
}


int main()
{
    constexpr StaticArray<unsigned, 10> array = {9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u};

    static_assert(array[0] == 9);
    static_assert(array[1] == 8);
    static_assert(array[2] == 7);
    static_assert(array[3] == 6);
    static_assert(array[4] == 5);
    static_assert(array[5] == 4);
    static_assert(array[6] == 3);
    static_assert(array[7] == 2);
    static_assert(array[8] == 1);
    static_assert(array[9] == 0);

    constexpr std::array<unsigned, 10> checker = {9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u};

    CONSTEXPR_LOOP<10>([&](auto i) constexpr {
        static_assert(array[i] == checker[i]);
    });

    return 0;
}

当我使用g++-8.3 编译它时,我得到了这个错误:

.../main.cpp: In instantiation of ‘main()::<lambda(auto:1)> [with auto:1 = std::integral_constant<long unsigned int, 0>]’:
.../main.cpp:9:10:   required from ‘constexpr void __loop(F&&, std::index_sequence<Is ...>) [with F = main()::<lambda(auto:1)>; long unsigned int ...Is = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; std::index_sequence<Is ...> = std::integer_sequence<long unsigned int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9>]’
.../main.cpp:16:11:   required from ‘constexpr void CONSTEXPR_LOOP(F&&) [with long unsigned int N = 10; F = main()::<lambda(auto:1)>]’
.../main.cpp:149:6:   required from here
.../main.cpp:148:32: error: non-constant condition for static assertion
         static_assert(array[i] == checker[i]);
                       ~~~~~~~~~^~~~~~~~~~~
.../main.cpp:148:32: error: ‘__closure’ is not a constant expression

在我花了一些时间了解问题所在后,我决定使用g++-7.4 编译这段代码。它编译成功,没有任何错误。 Clang-6 和 g++-9 给了我相同的结果,但是一旦我使用 g++-8,我就会得到上述错误。知道为什么会这样吗?

谢谢!

[注意]在线示例:https://godbolt.org/z/Ig4CCW

[UPDATE] 当我向 constexpr 变量添加静态说明符时,我在 g++-8 中编译了这段代码。它之所以有效,是因为:

enter link description here

如果变量,lambda 表达式可以使用变量而不捕获它

  • 是非局部变量或具有静态或线程本地存储持续时间>(在这种情况下无法捕获变量)

但是,如果您查看下面的代码,您会注意到从另一个函数调用的 lambda 出于某种原因没有通过 g++-8 中的引用和值捕获 constexpr 变量。其他编译器不报告任何错误。

template<typename F>
constexpr void call(F&& f)
{
    f();
}

int main()
{
    constexpr std::array<unsigned, 1> checker = {1u};
    call([&]() constexpr { static_assert(checker[0] == checker[0]); });
    static constexpr std::array<unsigned, 1> checker2 = {1u};
    call([]() constexpr { static_assert(checker2[0] == checker2[0]); });
    constexpr std::array<unsigned, 1> checker3 = {1u};
    call([=]() constexpr { static_assert(checker3[0] == checker3[0]); });

    return 0;
}

Try it

【问题讨论】:

  • 无论如何回答这个问题,你能解释一下为什么这甚至可以工作吗?
  • 附注:__loopreserved identifier,所以你不应该使用它。
  • Fixed in gcc 9.1。这是一个错误。
  • @JVApen 我可以根据自己的兴趣使用这个课程。例如:cpp template&lt;std::size_t ... Is&gt; constexpr auto f(std::index_sequence&lt;Is ...&gt;) noexcept { return StaticArray&lt;std::size_t, sizeof...(Is)&gt;(Is ...); } constexpr auto A = f(std::make_index_sequence&lt;15&gt;{}); CONSTEXPR_LOOP&lt;10&gt;([&amp;](auto i) constexpr { std::cout &lt;&lt; A[i] &lt;&lt; std::endl; }); 我知道有更好的方法可以做到这一点,但我想知道为什么这段代码在 g++8 中不起作用。
  • 如果突然坏了可能是回归。众所周知,Lambda 和 constexpr 难以实现。

标签: c++ templates lambda g++ c++17


【解决方案1】:

AFAIK 参数,即使在 constexpr 函数中也不是 constexpr

constexpr void f(std::size_t n) {
  static_assert(n == 42, "");  // not allowed.
}

来源:https://mpark.github.io/programming/2017/05/26/constexpr-function-parameters/

更新:来自 cmets

我被auto 愚弄了。确实,既然来电了:

func(std::integral_constant<std::size_t, Is>{}), ...);

autostd::integral_constant,它应该可以工作

【讨论】:

  • 你的答案不是问题的问题。
  • @MartinMorterol i 这是std::integral_constant,此代码适用于其他g++ 版本。
  • @MartinMorterol 如果我创建tempalte &lt;std :: size_t N&gt; constexpr const T &amp; operator [] (std :: integral_constant&lt;std :: size_t, I&gt; i) const noexcept,我会得到同样的错误。
  • 我实际上认为你的链接解释了为什么代码应该工作。
  • @HolyBlackCat 我在标准中挖掘。您认为i 由转换函数调用(隐式调用)计算是正确的:expr.ref 计算点或箭头之前的后缀表达式; 但没有左值到右值在评估 i 时评估转换。因此,即使对 i 求值,它也是一个常量表达式,请参阅 expr.const 中的长列表。 . (对于memo左值到右值的转换是访问对象值的评估)
【解决方案2】:

好吧,我认为这可能是g++8 中的一个错误。 Lambda 不会捕获 constexpr 变量,下面的代码非常清楚地证明了这一点:

template<typename F>
constexpr void call(F&& f)
{
    f();
}

int main()
{
    constexpr std::array<unsigned, 1> checker = {1u};
    call([&]() constexpr { static_assert(checker[0] == checker[0]); }); // compile error
    static constexpr std::array<unsigned, 1> checker2 = {1u};
    call([]() constexpr { static_assert(checker2[0] == checker2[0]); }); // OK!
    constexpr std::array<unsigned, 1> checker3 = {1u};
    call([=]() constexpr { static_assert(checker3[0] == checker3[0]); }); // compile error

    return 0;
}

我没有发现任何提及这个问题,所以我真的认为这是g++8中的一个错误。

另外,我找到了三个解决方案来避免这个错误。如果你得到同样的错误,你必须做三个之一:

  1. 将您的变量标记为static。 Lambda 可以使用静态变量而不进行捕获。
  2. 使用模板将 constexpr 变量包装在结构中:
template<std::size_t Size, typename T, T ... ARGS>
struct ArrayWrapper
{
   static constexpr std::array<T, Size> value = {ARGS ...};
};

constexpr ArrayWrapper<10, unsigned,
    9u, 8u, 7u, 6u, 5u, 4u, 3u, 2u, 1u, 0u> wrapper;
  1. 使用其他编译器。 g++-7g++-9clang 编译它没有任何错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 1970-01-01
    相关资源
    最近更新 更多