Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"if constexpr" interaction with "try in constexpr function" warning

I claim that this program ought to be well-formed: it declares a constexpr member function of S<int>. However, both GCC and Clang reject this program.

template<class T>
struct S {
    constexpr int foo() {
        if constexpr (std::is_same_v<T, int>) {
            return 0;
        } else {
            try {} catch (...) {}
            return 1;
        }
    }
};

int main()
{
    S<int> s;
    return s.foo();  // expect "return 0"
}

GCC says:

error: 'try' in 'constexpr' function

Clang says:

error: statement not allowed in constexpr function

Neither of them seem to notice that the "try" statement is located in a discarded branch of the if constexpr statement.

If I factor the try/catch out into a non-constexpr member function void trycatch(), then both Clang and GCC are happy with the code again, even though its behavior ought to be equivalent to the unhappy version.

template<class T>
struct S {
    void trycatch() {
        try {} catch (...) {}
    }
    constexpr int foo() {
        if constexpr (std::is_same_v<T, int>) {
            return 0;
        } else {
            trycatch();  // This is fine.
            return 1;
        }
    }
};

Is this

  • a bug in both GCC and Clang?
  • a defect in the Standard, which GCC and Clang are faithfully implementing?
  • a Quality of Implementation issue due to the "conditional constexprness" of foo()?

(Irrelevant background: I'm implementing constexpr any::emplace<T>() for an allocator-aware version of any whose allocator might be constexpr-per-P0639 (i.e. it might lack a deallocate member function) or might not. In the former case we don't want or need the try; in the latter case we need the try in order to call deallocate if the constructor of T throws.)

like image 383
Quuxplusone Avatar asked Oct 17 '17 00:10

Quuxplusone


People also ask

Can a constexpr function throw an exception?

Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.

What is the point of constexpr functions?

A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.

Is static needed for constexpr?

A non-static data member cannot be constexpr. static constexpr int x = 5; int y; };

Can a member function be constexpr?

const can only be used with non-static member functions whereas constexpr can be used with member and non-member functions, even with constructors but with condition that argument and return type must be of literal types.


1 Answers

The compilers are obeying the Standard. C++17 draft N4659 says ([dcl.constexpr]/(3.4.4)):

The definition of a constexpr function shall satisfy the following requirements:

  • ...

  • its function-body shall be = delete, = default, or a compound-statement that does not contain

    • ...

    • a try-block, or

    • ...

And none of the rules for "discarded statements" such as the else statement in your S<int>::foo override this rule. The only special things specified about discarded statements are that discarded statements are not instantiated, odr-uses within discarded statements do not cause a definition for the used declaration to be required, and discarded return statements are ignored when determining the true return type of a function with placeholder return type.

I did not see any existing C++ Issue discussing this, and the paper P0292R1 which proposed if constexpr does not address interactions with constexpr functions.

like image 73
aschepler Avatar answered Oct 26 '22 09:10

aschepler