In answer to my previous question I learned that C++ 20 concepts do not allow overloading on struct template arguments, for example this does not work:
#include <concepts>
template <std::integral>
struct S{
};
template <std::floating_point>
struct S{
};
For curious clang error is something like, but that does not matter since I know C++ standard does not allow this code to work:
type constraint differs in template redeclaration
I find this nonworking way of writing templates very natural so I wonder was this ever considered, and if so why it was rejected during standardization?
P.S. this seems to work in C++20, but I find it much uglier
#include <concepts>
#include <iostream>
template <typename T>
requires std::integral<T> || std::floating_point<T>
struct S{
};
template <std::integral T>
struct S<T>{
static constexpr char msg[] = "i";
};
template <std::floating_point T>
struct S<T>{
static constexpr char msg[] = "fp";
};
int main() {
std::cout << S<char>::msg << std::endl;
std::cout << S<double>::msg << std::endl;
}
C++ has never had overloading for classes or class templates. Classes of course have no parameters with which an overload might be chosen, but neither can one write
template<class> struct A {};
template<int> struct A {};
even though for every template-id it is obvious which would pertain (A<int> vs. A<1>). There are several reasons for this restriction:
A<…>, whether the argument is a type or a value is fixed even if it’s dependent. (This wouldn’t be true if the overloads were template<int&> and template<float&>, of course.)template<class...> class variety); another is CTAD.The C++20 behavior is just the continuation of this model; it’s not hard to add a generic
template<class> struct S; // undefined
to serve as an umbrella over partial specializations declared as
template<std::integral I>
struct S<I> {};
template<std::floating_point F>
struct S<F> {};
Template argument deduction does not work with an explicit specialization of a class template (in comparison to a function template). This has nothing to do with concepts per se. You will need the template<> keyword and the <T> parameter.
I do not know of and cannot find any standard proposal further shortening this.
In the following code especially the primary definition of S got much shorter than the working version in the question.
#include <concepts>
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;
template <Number T>
struct S;
template <std::integral T>
struct S<T>{
};
template <std::floating_point T>
struct S<T>{
};
The above code compiles and works.
By using Number the first line of the error message for instantiating S with e.g. std::string is:
error: template constraint failure for 'template<class T> requires Number<T> struct S'
Which is easy to understand and to the point (with more detailing information in the following lines about std::string not being an integral || floating_point). So it is possible to build levels of error messages for wrong instantiations (e.g. you could define your own integral concept which lists the allowed integer types).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With