Please refer to the following C 20 code:
template<bool op>
class Person
{
const bool own_pet;
public:
Person() : own_pet(op) {}
consteval bool OwnPet() const { return own_pet; }
consteval bool OwnPetC() const { return true; }
void PatPet() const {}
};
template<typename T>
concept MustOwnPet = requires(T obj) {
requires obj.OwnPet();
};
void pat(const MustOwnPet auto& p)
{
p.PatPet();
}
template<typename T>
concept MustOwnPetC = requires(T obj) {
requires obj.OwnPetC();
};
void pat_c(const MustOwnPetC auto& p)
{
p.PatPet();
}
int main()
{
// Error in gc 12.1 with -std=c 20:
// in 'constexpr' expansion of 'obj.Person<true>::OwnPet()'
// <source>:16:24: error: 'obj' is not a constant expression
// Also doesn't compile in clang 14.0.0 with -std=c 20
// pat(Person<true>());
// Error in clang 14.0.0 with -std=c 20
// Compiles fine in gcc 12.1 with -std=c 20
pat_c(Person<true>());
return 0;
}
My understanding is that both Person::OwnPet()
and Person::OwnPetC()
are compile time functions as op
is known as compile time as instantiated in main()
.
Then why the pat()
fails to compile? Please see error in the code comment.
And pat_c()
fails to compile only in clang
but compiles fine in gcc
. Which compiler is correct?
CodePudding user response:
The problem is that the parameter named obj
is not a constant expression. Thus it cannot be used in an evaluated context where a constant expression is required. For example, we cannot use obj
as a template nontype parameter(TNP) as TNP must be compile time constant which obj
is not.
Which compiler is correct?
This seems to be a bug in gcc submitted here
CodePudding user response:
Following code works with GCC 12.1
and -std=c 20
. I am not that familiar what you are trying to achieve but I guess the problem here is the member variable which is the same for all template instances and should therefore be a static constexpr
. If that is the case, OwnPet()
can be evaluated at compile time. In the other case it is logical for us humans that it can also be evaulated at compile time but I guess not for the compiler (because accessing a member variable instead of a const-expression)
template<bool op>
class Person
{
static constexpr bool own_pet = op;
public:
Person() {}
consteval bool OwnPet() const { return own_pet; }
consteval bool OwnPetC() const { return true; }
void PatPet() const {}
};
template<typename T>
concept MustOwnPet = requires(T obj) {
requires obj.OwnPet();
};
void pat(const MustOwnPet auto& p)
{
p.PatPet();
}
template<typename T>
concept MustOwnPetC = requires(T obj) {
requires obj.OwnPetC();
};
void pat_c(const MustOwnPetC auto& p)
{
p.PatPet();
}
int main()
{
pat(Person<true>());
pat_c(Person<true>());
return 0;
}