Clang 3.7.0 complains of class not being literal because it is not an aggregate and has no constexpr constructors

孤街醉人 提交于 2021-02-05 07:23:20

问题


The following code compiles fine in GCC (4.9.3) and VC++ (19.00.23506) but gives these error in Clang (3.7.0).

error: constexpr function's return type 'Foo' is not a literal type

note: 'Foo' is not literal because it is not an aggregate and has no constexpr constructors other than copy or move constructors

Code:

#include <iostream>
#include <vector>

struct Foo
{
    std::vector<int> m_vec;
    Foo(const int *foo, std::size_t size=0):m_vec(foo, foo+size)
    {;}
    //Foo(const std::initializer_list<int> &init):m_vec{init}
    //{;}
};


template <std::size_t N>
constexpr Foo make_fooArray(const int (&a)[N]) noexcept
{
    return {a,N};
}


int main()
{
    Foo f{ make_fooArray({1,2,3}) };

    for (auto i : f.m_vec)
        std::cout<< i <<" ";
    std::cout<<std::endl;
}

Code running on rextester:

GCC & VC

Clang

Can you please clarify whether this is a compiler bug or have I missed something? What does the C++11 standard say?


Here is one other case where it compiles in GCC and VC but not in Clang.

#include <iostream>

template <typename T, std::size_t N>
constexpr std::size_t sizeOf_fooArray(const T (&)[N]) noexcept
{
    return N;
}

int main()
{
    std::cout<< sizeOf_fooArray({16,20,53,87,54,7}) <<std::endl;
}

However, if you alias the int[] and explicitly use it to specify the type of the initializer_list, then it works in all compilers.

#include <iostream>

template <typename T, std::size_t N>
constexpr std::size_t sizeOf_fooArray(const T (&)[N]) noexcept
{
    return N;
}

using intArray = int[]; //Added

int main()
{
    std::cout<< sizeOf_fooArray(intArray{16,20,53,87,54,7}) <<std::endl;
}

回答1:


All the compilers are right.

With function templates, in general, it's possible that one instantiation meets the requirements for a constexpr function, but another doesn't. Normally, that means constexpr effectively gets ignored silently for those instantiations that don't meet the requirements. Example:

template <typename T> constexpr T f(T v) { return v; }

Both the f<int> and f<std::string> instantiations are valid, but f<std::string> cannot be called in a constant expression.

However, as an exception to that rule, if there isn't any possible template argument that could lead to an instantiation that meets the usual requirements for constexpr functions, the program is ill-formed, no diagnostic required. This means compilers are at liberty to ignore this rule entirely, but they are equally allowed to diagnose the code as a fatal error.

In general, it's not possible to reliably detect violations of this rule, that's why no diagnostic is required. Some compilers try harder than others to still give some diagnostics.

All of this is described in the standard in [dcl.constexpr]p6:

If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy the requirements for a constexpr function or constexpr constructor, that specialization is still a constexpr function or constexpr constructor, even though a call to such a function cannot appear in a constant expression. If no specialization of the template would satisfy the requirements for a constexpr function or constexpr constructor when considered as a non-template function or constructor, the template is ill-formed; no diagnostic required.



来源:https://stackoverflow.com/questions/40548269/clang-3-7-0-complains-of-class-not-being-literal-because-it-is-not-an-aggregate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!