constexpr expression and variable lifetime, an example where g++ and clang disagree

强颜欢笑 提交于 2019-12-09 05:47:45

问题


Consider the simple C++11 code:

template<int N>
struct Foo {};

template <int N>
constexpr int size(const Foo<N>&) { return N; }

template <int N>
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }

int main()
{
    Foo<5> foo;

    constexpr int x = size(foo);  // works with gcc and clang
                                  // _but_
    use_size(foo);                // the same statement in the use_size() 
                                  // function _only_ works for gcc
}

I can successfuly compile it with g++ -std=c++11 foo.cpp

however if I use clang++, clang++ -std=c++11 foo.cpp I get

foo.cpp:15:28: error: constexpr variable 'n' must be initialized by a constant expression
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }
                                                     ~~~~~^~~~
foo.cpp:23:5: note: in instantiation of function template specialization 'use_size<5>' requested here
    use_size(foo);                // the same statement in the use_size() 
    ^
1 error generated.

(nb: compiler versions. I have checked the previous statement with g++ version 5.3.1 and 7.2.1 and with clang++ version 3.6.2 and 5.0.0)

My question: which of g++ or clang is right? What is the problem?


回答1:


My interpretation is that clang++ is right and g++ is too permissive.

We can find a close example ([expr.const] section, page 126) in the standard https://isocpp.org/std/the-standard (draft can be downloaded, attention big PDF! ).

constexpr int g(int k) { 
    constexpr int x = incr(k); 

    return x; 
}

where it is explained that:

error: incr(k) is not a core constant expression because lifetime of k began outside the expression incr(k)

This is exactly what is happening in the use_size() function with the foo argument, even if the size() function only use the N template parameter.

template <int N>
constexpr int size(const Foo<N>&) { return N; }

template <int N>
void use_size(const Foo<N>& foo) { constexpr int n = size(foo); }



回答2:


I was expecting Clang to be wrong in this case. It should evaluate your function call as being a constant expression, simply because you use only the template parameter, and not the object itself. Since you don't use the object in your constexpr function, there should be nothing prohibit compile time evaluation.

However, there's a rule in the standard that says object that began their lifetime preceding the constant expression such as a reference is not useable as constexpr.

There is a simple fix in that case. I think it didn't like the reference:

template <int N> // pass by value, clang is happy
void use_size(Foo<N> foo) { constexpr int n = size(foo); }

Here's a live example

Alternatively, you can also copy your foo object and use that local object:

template <int N>
void use_size(const Foo<N>& foo) {
    auto f = foo;
    constexpr int n = size(f);
}

Live example



来源:https://stackoverflow.com/questions/47269936/constexpr-expression-and-variable-lifetime-an-example-where-g-and-clang-disag

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