Why do we need the two definitions: integral constant expression and converted constant expression?

前端 未结 3 1983
春和景丽
春和景丽 2021-02-04 03:49

§5.19/3 in C++14 defines an integral constant expression and a converted constant expression:

An integral constant expression is an expression

3条回答
  •  不知归路
    2021-02-04 04:30

    Note: this answer is based on the latest draft standard for now, known as N4567. Some differences between it and the C++11/14 standard are pointed out.

    integral constant expression and converted constant expression are different when class types are concerned. In C++98/03, when class types could not be used here (because there were no constexpr conversion functions at that time), there was indeed no such term as converted constant expression of type T.

    For an integral constant expression, the destination type is unknown. But for a converted constant expression of type T, the destination type is known to be T, and T is not necessarily an integral or unscoped enumeration type1.

    So, in order to compile a integral constant expression, the compiler first need to decide what the destination type is. If the expression has integral or unscoped enumeration type, then obviously the destination type is just the type of the expression. Otherwise, if the expression has a literal class type (let's call this type E), then the following process is used2:

    The compiler examines all the non-explicit conversion functions in E3. Let's say the result types of these functions forms a set S. If S contains exactly one integral or unscoped enumeration type (reference modifier is stripped and const and volatile qualifiers are ignored: const volatile int& is considered as int in this process), then the destination type is just that type. Otherwise, the determination fails.

    (It is important to note that in the aforementioned process, conversion function templates are not examined. )

    As a consequence, for example, if a class type has two conversion functions, one is constexpr operator int and the other is constexpr operator long, then this type cannot be used in a integral constant expression (the destination type is undecidable). However, such type may be used in a converted constant expression of type int or in a converted constant expression of type long.

    After deciding the destination type D, then overload resolution is applied to find the most appropriate conversion function or function template, and then the choosen conversion function (which must be constexpr) is called to produce a value of type D. — This part is, more or less, the same as a converted constant expression of type D.

    In the following example, Var{} is a valid integral constant expression, but is an invalid converted constant expression of type std::size_t (the example is inspired by this question).

    class Var
    {
    public:
    
        constexpr operator int ()
        { return 42; }
    
        template 
        constexpr operator T () = delete;
    };
    
    enum {
        x = Var{} // the initializer of `x` is expected to be an 
                  // integral constant expression
                  // x has value 42
    };
    
    int t[ Var{} ]; // the array bound is expected to be a
                    // converted constant expression of type std::size_t
                    // this declaration is ill-formed
    

    References

    N4567 5.20 [expr.const]p7

    If an expression of literal class type is used in a context where an integral constant expression is required, then that expression is contextually implicitly converted (Clause 4) to an integral or unscoped enumeration type and the selected conversion function shall be constexpr.

    N4567 4[conv]p5

    Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for non-explicit conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.

    Notes

    1. In C++11/14, a converted constant expression could only be of integral or enumeration type. N4268 changes that.
    2. In C++11, there were no such process, instead, it is required that "the class type shall have a single non-explicit conversion function to an integral or enumeration type and that conversion function shall be constexpr." N3323 changed that to the current wording.
    3. The word "non-explicit" did not exist in the C++14 standard. It was added by CWG 1981.

提交回复
热议问题