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

Clang warning about static templated constexpr (inline function is not defined)

本文关键字:未定义 警告 Clang 函数 于静态 静态 constexpr      更新时间:2023-10-16

我有以下 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;
}

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)

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

  1. 我在这里做傻事吗?
  2. gcc 版本是否在做我想的(在编译时用 As 初始化上部)?
  3. 这是叮当声中的错误吗?
  1. 我在这里做傻事了吗?

是的,该函数filledArray()总是调用非constexprstd::array:fill,因此将其声明为constexpr严格来说是一个错误(根据[dcl.constexpr]/5"程序格式不正确,不需要诊断")。

  1. gcc 版本是否在做我想的事情(在编译时用 As 初始化上部)?

许多编译器放宽了 [dcl.constexpr]/5 的要求,并在非 constexpr 上下文中使用时静默忽略constexpr。但是通过优化,他们也可以很容易地看到内联调用,例如std::arraystd::array::fill()的构造,并且很可能会评估你的函数编译时间,即使它没有被声明constexpr(demo)。

  1. 这是 clang 中的错误吗?

是的,这是一个叮当错误(#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;
}

f.me更改为foo::me也可以"修复"它。

作为解决方法,您可以将constexpr更改为const

  1. 它正在做我想的那样吗? -> 否(通过设置断点进行测试)

以下内容确实如此(灵感来自数组初始化编译时 - Constexpr 序列的答案)

#include <array>
#include <iostream>
#include <utility>
template <typename T, T value>
constexpr T generate_ith_number(const std::size_t) {
static_assert(std::is_integral<T>::value, "T must to be an integral type");
return value;
}
template <typename T, T value, T... Is>
constexpr auto make_sequence_impl(std::integer_sequence<T, Is...>)
{
return std::integer_sequence<T, generate_ith_number<T, value>(Is)...>{};
}
template <typename T, T value, std::size_t N>
constexpr auto make_sequence()
{
return make_sequence_impl<T, value>(std::make_integer_sequence<T, N>{});
}
template <typename T, T... Is>
constexpr auto make_array_from_sequence_impl(std::integer_sequence<T, Is...>)
{
return std::array<T, sizeof...(Is)>{Is...};
}
template <typename Seq>
constexpr auto make_array_from_sequence(Seq)
{
return make_array_from_sequence_impl(Seq{});
}
typedef unsigned char uchar;
class A {
public:
template <size_t N, uchar value>
static inline constexpr std::array<uchar, N> filledArray() {
return make_array_from_sequence(make_sequence<uchar, value, N>());
}
// long route
std::array<uchar, 5> upper = A::filledArray<5, 'A'>();
// taking a short cut
std::array<uchar, 45> blah = make_array_from_sequence_impl(make_sequence<uchar, 'A', 45>()); 
void dummy() {A::filledArray<5, 'A'>();}    // make clang happy
};
int main() {
A blah;
for (int i = 0; i < 5; ++i)
std::cout << blah.upper[i] << std::endl;
for (int i = 0; i < 45; ++i)
std::cout << blah.blah[i] << std::endl;
return 0;
}

这实际上也回答了#1。 是的,试图优化永远不会对性能至关重要的代码是愚蠢的,失败了,遇到编译器错误并浪费大量时间试图找到一个太冗长且难以阅读的解决方案。 :D

相关文章: