Home > database >  C 20 concept fails to compile when template class object instantiated with value
C 20 concept fails to compile when template class object instantiated with value

Time:07-15

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;
}
  • Related