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

时间:2017-12-16 17:29:13

标签: 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;
}

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或任何其他函数中使用正确的模板参数调用fillArray,则警告消失,clangOut也会按预期打印。

  1. 我在这里做些蠢事吗?
  2. 是我认为的gcc版本(在编译时使用As初始化上层)?
  3. 这是clang中的错误吗?

2 个答案:

答案 0 :(得分:3)

  
      
  1. 我在这里做些蠢事吗?
  2.   

是的,函数filledArray()总是调用非constexpr std::array:fill,因此声明constexpr严格来说是错误(根据[dcl.constexpr]/5“程序生病了 - 形成,无需诊断“)。

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

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

  
      
  1. 这是clang中的错误吗?
  2.   

是的,这是一个铿锵的错误(#18781)。

Clang无法编译constexpr类成员。 当这些元素被ODR使用时,它无法正确“看到”。要进行验证,您可以将static constexpr放在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

答案 1 :(得分:0)

  1. 这是我的想法吗? - &GT;否(通过设置断点进行测试)
  2. 以下内容(灵感来自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