简单的 constexpr 函数无法用 GCC 编译(clang 没问题)

Simple constexpr function failed to compile with GCC (clang is OK)

如下代码does not compile with GCC 5.2 (C++14). It does compile with clang 3.6 (C++14). (original code can be found here)

#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <utility>

template <typename T>
class aggregate_wrapper;

template <typename T, std::size_t n>
class aggregate_wrapper<T[n]> {
public:
  using array = T[n];

  template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
  aggregate_wrapper(Ts&&... xs)
      : arr_{std::forward<Ts>(xs)...} {
    // nop
  }

  aggregate_wrapper(const array& arr) {
    std::copy(arr, arr + n, arr_);
  }
  aggregate_wrapper(array&& arr) {
    std::move(arr, arr + n, arr_);
  }

  operator T* () {
    return arr_;
  }
  operator const T* () const {
    return arr_;
  }

  constexpr std::size_t size() const {
    return n;
  }

private:
  array arr_;
};

int main() {
  aggregate_wrapper<int[3]> arr;
  static_assert(arr.size() == 3, "");
}

产生的错误信息是

main.cpp: In function 'int main()':
main.cpp:44:3: error: non-constant condition for static assertion
   static_assert(arr.size() == 3, "");
   ^
main.cpp:44:25: error: call to non-constexpr function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]'
   static_assert(arr.size() == 3, "");
                         ^
main.cpp:34:25: note: 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not usable as a constexpr function because:
   constexpr std::size_t size() const {
                         ^
main.cpp:34:25: error: enclosing class of constexpr non-static member function 'constexpr std::size_t aggregate_wrapper<T [n]>::size() const [with T = int; long unsigned int n = 3ul; std::size_t = long unsigned int]' is not a literal type
main.cpp:10:7: note: 'aggregate_wrapper<int [3]>' is not literal because:
 class aggregate_wrapper<T[n]> {
       ^
main.cpp:10:7: note:   'aggregate_wrapper<int [3]>' is not an aggregate, does not have a trivial default constructor, and has no constexpr constructor that is not a copy or move constructor

有什么想法吗?代码是否应该按照标准编译?

为了让 g++ 编译它,您需要添加默认构造函数:

aggregate_wrapper() = default;

请在以下位置查看它的运行情况:http://coliru.stacked-crooked.com/a/df1ac057960bebc7

我感觉是 clang under the hood 添加了它,但我不是 100% 确定...

或者您可以将现有的可变参数构造函数用作 constexpr 构造函数来执行默认构造:

template <typename... Ts, typename = decltype(array{std::declval<Ts>()...})>
constexpr                      // <---- ADD THIS
aggregate_wrapper(Ts&&... xs)
   : arr_{std::forward<Ts>(xs)...} {
   // nop
}

海湾合作委员会是错误的。它的诊断部分说:

main.cpp:34:25: note: '<...>' is not usable as a constexpr function because:
main.cpp:34:25: error: enclosing class of constexpr non-static member function '<...>' is not a literal type

...但是没有这样的规则。请参阅 [dcl.constexpr]/3 了解适用于此处的限制列表。

您可以通过添加虚拟 constexpr 构造函数来绕过伪造的 GCC 诊断(如果您不希望任何真正的构造函数为 constexpr) 或使 sizestatic.