I've been playing around with C++20 concepts and ran into an issue with my GCC 9.4 compiler, which I ultimately want to support. I created this minimal reproducible example to showcase the problem:
#include <iostream>#include <type_traits>template <typename T, typename Type>concept IsSame = std::is_same_v<T, Type>;template <typename Scalar>class MyClass { public: template <typename T> using ScalarT = T; template <typename T> requires IsSame<T, typename MyClass<Scalar>::template ScalarT<double>> static void print(const T& b);};// Compilation depends on compilertemplate<typename Scalar>template<typename T>requires IsSame<T, typename MyClass<Scalar>::template ScalarT<double>>void MyClass<Scalar>::print(const T& t) { std::cout << std::fixed << t << " as Scalar is " << std::fixed << static_cast<Scalar>(t) << std::endl;}int main() { MyClass<int>::print(6.0); return 0;}
GCC 9.4 (with -std=c++2a -fconcepts
) and the more recent GCC 14.2 (with -std=c++20
) complain that:
<source>:334:6: error: no declaration matches 'void MyClass<Scalar>::print(const B&)' 334 | void MyClass<Scalar>::print(const B& b) { | ^~~~~~~~~~~~~~~<source>:327:17: note: candidate is: 'template<class Scalar> template<class T> requires IsSame<T, double> static void MyClass<Scalar>::print(const T&)' 327 | static void print(const T& b); | ^~~~~<source>:320:7: note: 'class MyClass<Scalar>' defined here 320 | class MyClass {
However, both GCC compilers are ok with defining the function inline, as:
template <typename T> requires IsSame<T, typename MyClass<Scalar>::template ScalarT<double>> static void print(const T& t) { std::cout << std::fixed << t << " as Scalar is " << std::fixed << static_cast<Scalar>(t) << std::endl; }
or
template <typename T> requires IsSame<T, ScalarT<double>> static void print(const T& t) { std::cout << std::fixed << t << " as Scalar is " << std::fixed << static_cast<Scalar>(t) << std::endl; }
Clang 19.1 (with -std=c++2a
) has no issue, and prints 6.000000 as Scalar is 6
.
I know I could also do the following:
template <IsSame<ScalarT<double>> T> static void print(const T& t) { std::cout << std::fixed << t << " as Scalar is " << std::fixed << static_cast<Scalar>(t) << std::endl; }
But this has the same issue with GCC, where moving the implementation outside the class does not work:
template<typename Scalar>template<IsSame<typename MyClass<Scalar>::template ScalarT<double>> T>static void MyClass<Scalar>::print(const T& t) { std::cout << std::fixed << t << " as Scalar is " << std::fixed << static_cast<Scalar>(t) << std::endl;}