Search code examples
c++templatesc++20consteval

While template deduction, is it possible to detect if a consteval function could be run


Let's say we have a consteval function or a trivial struct with consteval construnctor, which only accept some of the values:

struct A
{
    consteval A(int a)
    {
        // compile error if a < 0
        if (a < 0)
            throw "error";
    }
};

Is there any way to detect if a non-type template parameter of int could be accepted by such a constructor? I have tried the following code but failed.

template <int a> concept accepted_by_A = requires() {A(a);};

int main()
{
    std::cout << accepted_by_A<-1> << std::endl; // output 1 (true)
}

Solution

  • Since a call to a constexpr (or consteval) function that throws is not a constant expression, you can detect this:

    template<int I> concept accepted_by_A=
      requires() {typename std::type_identity_t<int[(A(I),1)]>;};
    
    static_assert(accepted_by_A<1>);
    static_assert(!accepted_by_A<-1>);
    

    It's also possible to use SFINAE (which would allow it to work with constexpr in previous language versions):

    template<int,class=void> struct test : std::false_type {};
    template<int I> struct test<I,decltype(void((int(*)[(A(I),1)])nullptr))> : std::true_type {};
    
    static_assert(test<1>());
    static_assert(!test<-1>());
    

    An alternate specialization

    template<int I> struct test<I,typename voided<int[(A(I),1)]>::type> : std::true_type {};
    

    seems like it ought to work but doesn't; substitution of non-type template arguments is weird in some cases.