关于静态模板化 constexpr 的 Clang 警告(未定义内联函数)

Ada*_*dam 6 c++ gcc clang

我有以下 C++ 代码:

#include <array>
#include <iostream>

typedef unsigned char uchar;

class A {
public:
    template <size_t N, uchar value>
    static inline constexpr std::array<uchar, N> filledArray() {
        std::array<uchar,N> ret{};
        ret.fill(value);
        return ret;
    }

    std::array<uchar, 5> upper = A::filledArray<5, 'A'>();
};

int main() {
    A blah;
    for (int i = 0; i < 5; ++i)
        std::cout << blah.upper[i] << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

g++ 编译它时没有警告,并且输出为 As,正如预期的那样。但 clang++-4.0 产生:

clang++-4.0 -std=c++14 main.cpp -o clangOut
main.cpp:9:47: warning: inline function 'A::filledArray<5, 'A'>' is not defined [-Wundefined-inline]
        static inline constexpr std::array<uchar, N> filledArray() {
                                                    ^
main.cpp:15:34: note: used here
        std::array<uchar, 5> upper = A::filledArray<5, 'A'>();
                                        ^
1 warning generated.
/tmp/main-b6fac8.o: In function `A::A()':
main.cpp:(.text._ZN1AC2Ev[_ZN1AC2Ev]+0x15): undefined reference to `std::array<unsigned char, 5ul> A::filledArray<5ul, (unsigned char)65>()'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Run Code Online (Sandbox Code Playgroud)

似乎 clang 没有看到,我实例化了filledArray 函数。如果我在主函数或任何其他函数中使用正确的模板参数调用filledArray,警告就会消失,并且 clangOut 也会按预期打印。

  1. 我在这里做了什么蠢事吗?
  2. gcc 版本是否按照我的想法执行(在编译时使用 As 初始化 upper)?
  3. 这是 clang 中的错误吗?

rus*_*tyx 5

  1. 我在这里做了什么蠢事吗?

是的,该函数filledArray()总是调用非 constexpr std::array:fill,因此constexpr严格来说声明它是一个错误(根据[dcl.constexpr]/5 “程序格式错误,无需诊断”)。

  1. gcc 版本是否按照我的想法执行(在编译时使用 As 初始化 upper)?

许多编译器放宽了[dcl.constexpr]/5constexpr要求,并在非 constexpr 上下文中使用它时默默地忽略它。std::array但通过优化,它们还可以轻松地查看内联调用,例如和的构造std::array::fill(),并且很可能会在编译时评估您的函数,即使它没有声明constexprdemo)。

  1. 这是 clang 中的错误吗?

是的,这是一个 clang bug ( #18781 )。

Clang 无法编译static constexpr类成员。当 ODR 使用此类元素时,它无法正确“查看”。A::filledArray<5, 'A'>();为了验证,您可以将其自身放置在 内部的某个位置main(),这将“修复”编译(但不会修复格式错误)。

另一个例子:

#include <iostream>

struct foo
{
  constexpr static const char* me = "foo";
};

int main ()
{
  foo f;
  std::cout << f.me << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

更改f.mefoo::me也可以“修复”它。

作为解决方法,您可以更改constexprconst.