I am fairly new to C 20 Concepts and I am experimenting with creating a template class Foo
with fully specialised implementations based on a non-type (enumerator) template parameter. I have tested my idea using the code below:
// Foo.h
#include <concepts>
#include <iostream>
#include <type_traits>
enum class Type { A, B, C };
template<std::is_enum T1, std::is_enum T2>
constexpr bool isEnumSame() { return T1 == T2; }
template<Type T> concept isTypeA = isEnumSame<T, Type::A>;
template<Type T> concept isTypeB = isEnumSame<T, Type::B>;
template<Type T>
struct Foo
{
Foo() { std::cout << "Generic Foo\n"; }
};
template<Type T>
requires isTypeA<T>
struct Foo<T>
{
Foo() { std::cout << "Type A Foo\n"; }
};
template<Type T>
requires isTypeB<T>
struct Foo<T>
{
Foo() { std::cout << "Type B Foo\n"; }
};
// Foo.cpp
#include "Foo.h"
int main(void)
{
Foo<Type::C> testC;
Foo<Type::A> testA;
Foo<Type::B> testB;
return 0;
}
However, I get compiler errors like the following (I'm using g 11 and gcc11):
error: insufficient contextual information to determine type
27 | Foo<Type::C> testC;
and similarly for Foo<Type::A> testA
and Foo<Type::B> testB
. Can someone advise me on what I may be doing incorrectly here? Also, I would ideally like to define the full specialisations similar to:
template<isTypeA T>
struct Foo<T>
{
Foo() { Print("Type A Foo"); }
};
but the above yields the compiler error error: ‘isTypeA’ does not constrain a type
. Any help would be much appreciated!
CodePudding user response:
std::is_enum
is not a concept, so this predicate does not do what I assume you think it does:
template<std::is_enum T1, std::is_enum T2>
constexpr bool isEnumSame() { return T1 == T2; }
You can take Type
values directly. Also, your concept lines need to call the functions like so:
template<Type T1, Type T2>
constexpr bool isEnumSame() { return T1 == T2; }
template<Type T> concept isTypeA = isEnumSame<T, Type::A>();
template<Type T> concept isTypeB = isEnumSame<T, Type::B>();
If you want your isEnumSame
to be generic over any enumeration:
template <class T>
concept Enumeration = std::is_enum_v<T>;
template<Enumeration auto T1, Enumeration auto T2>
constexpr bool isEnumSame() { return T1 == T2; }
CodePudding user response:
There are several problems:
-
template<std::is_enum T1, std::is_enum T2>
is_enum
is a trait, not a concept. You can use something likeauto T1
, and thenrequires std::is_enum_v<decltype(T1)>
. -
template<Type T> concept isTypeA = isEnumSame<T, Type::A>;`
You forgot to call the function, add
()
.
But you don't actually need any of this concept magic. You can fully specialize Foo
for the enum values:
template <>
struct Foo<Type::A> {...};
If you for some reason don't want a full specialization, you can do this:
template <Type T>
requires (T == Type::A)
struct Foo<T> {...};
CodePudding user response:
First, std::is_enum
is not a concept, it is just a normal class,
so a syntax like template<std::is_enum T1, std::is_enum T2>
just defines a template function that accepts a non-type template parameter of type std::is_enum
, which is not what you might expect and doesn't constrain anything.
Second, the template parameter of bool isEnumSame()
is not a type but a enum value, so it should be defined as template<auto T1, auto T2>
.
If you need to constrain the type of the non-type template parameter, you can just use decltype
to get its type and use requires-expression to constrain it, like this:
enum class Type { A, B, C };
template<auto T1, auto T2>
requires std::is_enum_v<decltype(T1)> && std::is_enum_v<decltype(T2)>
constexpr bool isEnumSame() { return T1 == T2; }
template<Type T> concept isTypeA = isEnumSame<T, Type::A>();
template<Type T> concept isTypeB = isEnumSame<T, Type::B>();