Description of problem: "static constexpr" does not be evaluated at compiling time with "-std=c++14" on aarch64. Version-Release number of selected component (if applicable): gcc-c++-11.2.1-9.4.el9.aarch64 gcc-c++-11.3.1-2.1.el9.aarch64 How reproducible: 100% Steps to Reproduce: 1. edit errdemo.cc //////////// #include <functional> #include <cstdlib> template<typename SSL_TYPE> struct TypeTraits {}; template<> struct TypeTraits<void> { static constexpr auto kFreeFunc = &free; }; int main() { std::function<void(void*)> x = TypeTraits<void>::kFreeFunc; } //////////// 2. compile it with -std=c++14 -lcrypt #] g++ -std=c++14 errdemo.cc Actual results: /bin/ld: /tmp/ccBvnuK4.o: in function `main': errdemo.cc:(.text+0xc): undefined reference to `TypeTraits<void>::kFreeFunc' /bin/ld: errdemo.cc:(.text+0x10): undefined reference to `TypeTraits<void>::kFreeFunc' collect2: error: ld returned 1 exit status Expected results: Just compile success Additional info: I have tested many case: It happens only with gcc11 -std=c++14 or gcc11 -std=c++11. All the following case is OK: gcc11 -std=c++17 gcc11 -std=c++14 -O2 gcc11 -std=c++11 -O2 gcc10 -std=c++17 gcc10 -std=c++14 gcc10 -std=c++11 gcc8 -std=c++17 gcc8 -std=c++14 gcc8 -std=c++11 gcc4.8 -std=c++11
Adding this decltype(SslTypeTraits<X509>::kFreeFunc) SslTypeTraits<X509>::kFreeFunc; appears to fix it. I suspect the std::function constructor causes kFreeFunc to be ODR-used. In that case, it's a source code bug.
You mean this may be a bug of libstdc++ instead of gcc compiler itself? BTW, it wont happens with "-O2" or on x86_64.
And your method is very hard to be used in template header files
(In reply to Kirby Zhou from comment #2) > You mean this may be a bug of libstdc++ instead of gcc compiler itself? I mean a bug in your source code. The issue is that in your updated reproducer, the argument type of the std::function constructor is deduced as void (* const&)(void*) noexcept and the const reference makes kFreeFunc ODR-used, requiring the separate, out-of-line definition of kFreeFunc. > BTW, it wont happens with "-O2" or on x86_64. C++ does not require diagnostics for these issues. (In reply to Kirby Zhou from comment #3) > And your method is very hard to be used in template header files With the updated source code, you could try this: std::function<void(void*)> x = +TypeTraits<void>::kFreeFunc; This causes the std::function constructor to bind to a temporary.
However, why -std=c++17 can pass with and without "+TypeTraits"? Both c++14 and c++17, the constructors are same: std::function<void (void*)>::function<void (* const&)(void*) noexcept, void>(void (* const&)(void*) noexcept) And under RHEL8, gcc-8 can also pass with and without "+TypeTraits", with -std=c++11 or -std=c++14 And under RHEL7, gcc-4.8 "-std=c++11" can pass without "+TypeTraits", but can not pass with "+TypeTraits". [kirbyzhou@el7dev_kirbyzhou constexpr]$ g++ -std=c++11 errdemo.cc /tmp/ccuoTuqM.o: In function `main': errdemo.cc:(.text+0x8): undefined reference to `TypeTraits<void>::kFreeFunc' errdemo.cc:(.text+0xc): undefined reference to `TypeTraits<void>::kFreeFunc' collect2: error: ld returned 1 exit status [kirbyzhou@el7dev_kirbyzhou constexpr]$ cat errdemo.cc #include <functional> #include <cstdlib> template<typename SSL_TYPE> struct TypeTraits {}; template<> struct TypeTraits<void> { static constexpr auto kFreeFunc = &free; }; int main() { std::function<void(void*)> x = +TypeTraits<void>::kFreeFunc; }
In C++17, static constexpr data members are implicitly inline (which makes them definitions). So with -std=c++17 the code works because in C++17, TypeTraits<void>::kFreeFunc is a definition, but in C++14 it's only a declaration, and as Florian said, since TypeTraits<void>::kFreeFunc is ODR-used, a definition is needed. See https://developers.redhat.com/articles/2021/08/06/porting-your-code-c17-gcc-11#static_constexpr_and_consteval_class_members_implicitly_inline In C++14, you could make TypeTraits<void>::kFreeFunc a definition like this (unfortunately without using 'auto'): #include <functional> #include <cstdlib> template<typename SSL_TYPE> struct TypeTraits {}; template<> struct TypeTraits<void> { static constexpr void (*kFreeFunc) (void *) = &free; }; constexpr void (*TypeTraits<void>::kFreeFunc) (void *); int main() { std::function<void(void*)> x = TypeTraits<void>::kFreeFunc; } Not a bug.
I get your point. But: Under RHEL8, gcc-8 can also pass with and without "+TypeTraits", with -std=c++11 or -std=c++14 Under RHEL7, gcc-4.8 "-std=c++11" can pass without "+TypeTraits", but can not pass with "+TypeTraits". So the behavior of "gcc-8 -std=c++14" "gcc-8 -std=c++11" ""gcc-4.8 -std=c++11" is wrong ? Or it is just undefined behavior?
For correction, It happens on gcc11 on x86_64 too. I have tested many case: It happens only with gcc11 -std=c++14 or gcc11 -std=c++11. All the following case are OK: gcc11 -std=c++17 gcc11 -std=c++14 -O2 gcc11 -std=c++11 -O2 gcc10 -std=c++17 gcc10 -std=c++14 gcc10 -std=c++11 gcc8 -std=c++17 gcc8 -std=c++14 gcc8 -std=c++11 gcc4.8 -std=c++11 Please reconsider whether this situation is reasonable.
There is nothing to reconsider, that is simply how C++11/14 vs. C++17/20 works. When TypeTraits<void>::kFreeFunc is ODR-used, it needs to be defined, not just declared. If you don't do that, your program isn't valid C++. It might still compile and link, but might not.