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

问题描述 投票:3回答:2

我有以下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是看不到的,我实例化了fillArray函数。如果我在main或任何其他函数中使用正确的模板参数调用fillArray,警告将消失,clangOut也会按预期打印。

  1. 我在这做傻事吗?
  2. 是gcc版本做我认为(在编译时用As初始化上层)?
  3. 这是clang中的一个错误吗?
c++ gcc clang
2个回答
3
投票
  1. 我在这做傻事吗?

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

  1. 是gcc版本做我认为(在编译时用As初始化上层)?

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

  1. 这是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;
}

f.me改为foo::me也“修复”了它。

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


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

以下内容(灵感来自Array Initialisation Compile Time - Constexpr Sequence的答案)

#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

© www.soinside.com 2019 - 2024. All rights reserved.