Three-way comparison and constexpr function template: which compiler is right? - c++

Consider:
#include <compare>
template<class=void>
constexpr int f() { return 1; }
unsigned int x;
using T = decltype(x <=> f());
GCC and MSVC accept the declaration of T. Clang rejects it, with the following error message:
<source>:7:26: error: argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'
using T = decltype(x <=> f());
^
1 error generated.
(live demo)
If the template-head (template<class=void>) is removed, or if f is explicitly or implicitly instantiated before the declaration of T, then Clang accepts it. For example, Clang accepts:
#include <compare>
template<class=void>
constexpr int f() { return 1; }
unsigned x;
auto _ = x <=> f();
using T = decltype(x <=> f());
(live demo)
Which compiler is correct, and why?

Clang is correct per N4861.
[temp.inst]/5:
Unless a function template specialization is a declared specialization, the function template specialization is implicitly instantiated when the specialization is referenced in a context that requires a function definition to exist or if the existence of the definition affects the semantics of the program.
[temp.inst]/8:
The existence of a definition of a variable or function is considered to affect the semantics of the program if the variable or function is needed for constant evaluation by an expression ([expr.const])
[expr.const]/15:
A function or variable is needed for constant evaluation if it is:
a constexpr function that is named by an expression ([basic.def.odr]) that is potentially constant evaluated, or
a variable [...].
[expr.const]/15:
An expression or conversion is potentially constant evaluated if it is:
a manifestly constant-evaluated expression,
a potentially-evaluated expression ([basic.def.odr]),
an immediate subexpression of a braced-init-list,
an expression of the form & cast-expression that occurs within a templated entity, or
a subexpression of one of the above that is not a subexpression of a nested unevaluated operand.
[expr.const]/5:
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
[...]
an invocation of an undefined constexpr function;
[dcl.init.list]/7:
A narrowing conversion is an implicit conversion
[...]
from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type
[expr.spaceship]/4:
If both operands have arithmetic types, or one operand has integral type and the other operand has unscoped enumeration type, the usual arithmetic conversions are applied to the operands.
Then:
If a narrowing conversion is required, other than from an integral type to a floating-point type, the program is ill-formed.
[expr.arith.conv]:
[T]he usual arithmetic conversions [...] are defined as follows:
[...]
Otherwise, the integral promotions ([conv.prom]) shall be performed on both operands.
Then the following rules shall be applied to the promoted operands:
[...]
Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.
Since x <=> f() in decltype(x <=> f()) does not satisfy the criteria of being "potentially constant evaluated", f is not "needed for constant evaluation". Therefore, the existence of the definition of f<> is not considered to affect the semantics of the program. Therefore, this expression does not instantiate the definition of f<>.
Therefore, in the original example, f() is a call to undefined constexpr function, which is not a constant expression.
Per the usual arithmetic conversions, in x <=> f(), f() (of type int) is converted to unsigned int. When f() is not a constant expression, this conversion is a narrowing conversion, which renders the program ill-formed.
If f is not a function template, or if its definition has been instantiated, then f() is a constant expression, and because the result of f() fits into unsigned int, the conversion from f() to unsigned int is not a narrowing conversion, and thus the program is well-formed.

Related

Does lvalue-to-rvalue conversion is applied to non-type lvalue template argument?

struct S{
constexpr S() {};
};
template <auto x> void f();
int main() {
S s{};
f<s>();
}
First off, per [temp.arg.nontype]/1
If the type T of a template-parameter (13.2) contains a placeholder
type (9.2.9.6) or a placeholder for a deduced class type (9.2.9.7),
the type of the parameter is the type deduced for the variable x in
the invented declaration
T x = template-argument;
If a deduced parameter type is not permitted for a template-parameter
declaration (13.2), the program is ill-formed.
Our template parameter contains a placeholder type auto, so the type of the parameter is the type deduced for the variable x in the invented declaration auto x = s; In this case, the type of the parameter is S, and S is a permitted type for the parameter declaration because S is a structural literal type.
Second, per [temp.arg.nontype]/2
A template-argument for a non-type template-parameter shall be a
converted constant expression (7.7) of the type of the
template-parameter.
This means the template-argument s shall be a converted constant expression. So per [expr.const]/10:
A converted constant expression of type T is an expression, implicitly
converted to type T, where the converted expression is a constant
expression and the implicit conversion sequence contains only
[..]
(10.2) — lvalue-to-rvalue conversions (7.3.2)
[..]
I'm not sure whether or not the lvalue s is converted to prvalue before any implicit conversions are applied to it. Note the definition of converted constant expression in C++14 is relatively changed. N4140 §5.19 [expr.const]/3: (emphasis mine)
A converted constant expression of type T is an expression, implicitly
converted to a prvalue of type T, where the converted expression is a
core constant expression and the implicit conversion sequence contains
only [..]
So per C++14, It's guaranteed that the converted expression is converted to a prvalue before any implicit conversion is applied to it.
I'm not so sure whether or not an lvalue-to-rvalue conversion is applied to template-argument s. In other words, I'm not sure that the lvalue s is converted to a prvalue.
So in general, if I passed an lvalue as a template argument for a non-type template parameter, does an lvalue-to-rvalue conversion applied to that lvalue?
And aside from that, Is the object s constant-initialized?
The meaning of [expr.const]/10 is that whenever an expression appears in a context that requires a "converted constant expression of type T", that expression is "implicitly converted to type T" and the additional restrictions in [expr.const]/10 shall apply.
So s is not "converted to a prvalue before any implicit conversions are applied to it". Rather, s is implicitly converted to type S, and this implicit conversion might involve some standard and/or user-defined conversions, such as an lvalue-to-rvalue conversion. To know whether or not it does, we would have to look at the rules for implicit conversions, which are in [conv.general]. In particular [conv.general]/6 states that
The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type ([dcl.ref]), an xvalue if T is an rvalue reference to object type, and a prvalue otherwise.
The expression E is used as a glvalue if and only if the initialization uses it as a glvalue.
I believe the wording here is a relic of older standard editions. What it should say (in modern language) is that when T is a non-reference type, an implicit conversion of E to T yields a prvalue that initializes its result object (call it t) as if by T t = E;.
So we consider S t = s; What does this initialization do? It calls the copy constructor of S to initialize t; [dcl.init.general]/16.6.2.1. There is no lvalue-to-rvalue conversion in this scenario. (Lvalue-to-rvalue conversions on class types are rare, but do occur in some places in the language, e.g., [expr.call]/12, [expr.cond]/7. If an lvalue-to-rvalue conversion were performed on s, it would also call the copy constructor; [conv.lval]/3.2. But in this particular case, the rules of the language do not require an lvalue-to-rvalue conversion.)
Consequently, the result of using s as a template argument for a template parameter of type S is that a prvalue is generated that initializes its result object by calling the copy constructor with s as the argument. (This particular copy constructor is implicitly defined to not do anything, since there are no subobjects to copy.)
This answers your question regarding whether an lvalue-to-rvalue conversion is applied. (You might be wondering what actually happens to the prvalue and whether the copy constructor actually gets called, but that's a separate topic that I don't want to get into right now, because it would make this answer too long.)
As for whether s is constant-initialized, yes it is. It satisfies (2.1) (since it has an initializer) and (2.2) since the full-expression of its initialization is a constant expression (there is nothing to stop it from being a constant expression, since it does nothing other than calling the constexpr default constructor, which itself does nothing). I'm not sure how this is relevant to your other question, though.

integral_constants in ternary operator

MSVC and clang/gcc disagree on whether two different integral constants can be used in a ternary operator (and as a consequence whether they have a common_type):
#include <utility>
int main()
{
return false
? std::integral_constant<int, 1>()
: std::integral_constant<int, 2>();
}
The above snippet compiles fine in clang and gcc, but not in MSVC. What is the correct behavior according to the standard? And if it's clang/gcc behavior then what are the conversion sequences used to deduce the common type of these two distinct types?
tldr; The code is well-formed. The conditional expression will have type int and value 2. This is an MSVC bug.
From [expr.cond]:
Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or [...], an attempt is made to form an implicit conversion sequence (13.3.3.1) from each of those operands to the type of the other. [...] Attempts are made to form an implicit conversion sequence from an operand expression E1 of type T1 to a target type related to the type T2 of the operand expression E2 as follows: [...]
— If E2 is an lvalue, [...]
— If E2 is an xvalue, [...]
— If E2 is a prvalue or if neither of the conversion sequences above can be formed and at least one of the
operands has (possibly cv-qualified) class type:
— if T1 and T2 are the same class type (ignoring cv-qualification), or one is a base class of the other,
and T2 is at least as cv-qualified as T1, the target type is T2,
— otherwise, the target type is the type that E2 would have after applying the lvalue-to-rvalue (4.1),
array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions.
So we're trying to form an implicit conversion sequence from type std::integral_constant<int, 1> to type std::integral_constant<int, 2>. That's not viable. Nor is the implicit conversion sequence in the reverse direction viable either. These types are simply not interconvertible.
So we continue:
If no conversion
sequence can be formed, the operands are left unchanged and further checking is performed as described
below. [...]
If the second and third operands are glvalues of the same value category and have the same type, [...]
Otherwise, the result is a prvalue. If the second and third operands do not have the same type, and either
has (possibly cv-qualified) class type, overload resolution is used to determine the conversions (if any) to be
applied to the operands (13.3.1.2, 13.6). If the overload resolution fails, the program is ill-formed.
Ok, what overload resolution can we perform? From [over.match.oper]:
If either operand has a type that is a class or an enumeration, a user-defined operator function might be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to
a type that is appropriate for a built-in operator.
Where the builtins are specified in [over.built] as:
For every pair of promoted arithmetic types L and R, there exist candidate operator functions of the form
LR operator?:(bool, L , R );
where LR is the result of the usual arithmetic conversions between types L and R.
One of those builtins would be int operator?:(bool, int, int). Since std::integral_constant<int, V> does have an operator int(), this is a viable conversion for both arguments.
We continue in [expr.cond]:
Otherwise, the conversions thus determined are applied, and the converted operands are used in place of the original operands for the remainder of this section.
Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed
on the second and third operands. After those conversions, one of the following shall hold:
— The second and third operands have the same type; the result is of that type and the result object is initialized using the selected operand.
At this point, the second and third operands do have the same type: int. So the result object is initialized as an int, and the expression is well-formed.
The relevant paragraph from [expr.cond] is 6:
Otherwise, the result is a prvalue. If the second and third operands
do not have the same type, and either has (possibly cv-qualified)
class type, overload resolution is used to determine the conversions
(if any) to be applied to the operands (13.3.1.2, 13.6). If the
overload resolution fails, the program is ill-formed. Otherwise, the
conversions thus determined are applied, and the converted operands
are used in place of the original operands for the remainder of this
section.
integral_constant<int> has a conversion operator to int, so this can work.
Following through to 13.3.1.2, we see that by paragraph 3.2, all builtin ?: operators taking integer and floating point arguments are candidates.
Now overload resolution is performed for all these, given our three arguments. As per [over.ics.rank]/3.3, we tie-break by comparing the standard conversion sequences from int (the return type of integral_constant<int>'s conversion operator) to the builtin operators' parameter types.
However, a look at table 13 is enough; conversions to the floating point types have Conversion rank, and since int is a promoted type, converting to any integral type but int (which is an identity conversion) is an integral conversion with Conversion rank. Hence the best viable candidate is, unambiguously, operator?:(bool, int, int). That is, MSVC is wrong.

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

§5.19/3 in C++14 defines an integral constant expression and a converted constant expression:
An integral constant expression is an expression of integral or
unscoped enumeration type, implicitly converted to a prvalue, where
the converted expression is a core constant expression. [ Note: Such
expressions may be used as array bounds (8.3.4, 5.3.4), as bit-field
lengths (9.6), as enumerator initializers if the underlying type is
not fixed (7.2), and as alignments (7.6.2). —end note ] A converted
constant expression of type T is an expression, implicitly
converted to a prvalue of type T, where the converted expression is
a core constant expression and the implicit conversion sequence
contains only user-defined conversions, lvalue-to-rvalue conversions
(4.1), integral promotions (4.5), and integral conversions (4.7) other
than narrowing conversions (8.5.4). [ Note: such expressions may be
used in new expressions (5.3.4), as case expressions (6.4.2), as
enumerator initializers if the underlying type is fixed (7.2), as
array bounds (8.3.4), and as integral or enumeration non-type template
arguments (14.3). —end note ]
Maybe I'm missing something, but my first impression is that every integral constant expression is a converted constant expression.
Edit
And I also believe there is an error in this paragraph:
Instead of:
A converted constant expression of type T is an expression, implicitly converted to a prvalue of type T, ...
it should be:
A converted constant expression of type T is an expression, implicitly converted to a prvalue of an integral type, ...
And this change allows the following code to compile:
#include <iostream>
struct A { operator int() { return 5; } } a;
int main() {
int b[a]{ 0, 1, 2, 3, 4 };
std::cout << b[4] << '\n';
}
where a in the declaration int b[a]{ 0, 1, 2, 3, 4}; is a converted constant expression of type A, implicitly converted to a prvalue of integral type (int) where the converted expression 5 is a core constant expression, and the implicit conversion sequence contains only a user-defined conversion.
Both definitions are needed because there are things you can do with one but not the other. And no, not every integral constant expression is really a converted constant expression. For the obvious example, a converted constant expression prohibits narrowing conversions, but an integral constant expression doesn't.
Therefore I can't do this:
enum x : char { a = 1024 };
If, however the initializer for an enum allowed an integral constant expression, rather than a converted constant expression, precisely that would be allowed.
As a Venn diagram, I'd draw the situation something like this:
So, there is quite a bit of overlap between the two (probably more than this diagram implies) but each allows at least a few things the other doesn't. I've given an example of one item in each direction, but haven't tried to list the differences exhaustively.
I'm not entirely convinced about user-defined conversions being prohibited for integral constant expressions though (and a quick test shows that the compilers I have handy at the moment allow them). That would give the situation as I originally wrote this answer, which would be more like this:
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 <typename T>
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
In C++11/14, a converted constant expression could only be of integral or enumeration type. N4268 changes that.
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.
The word "non-explicit" did not exist in the C++14 standard. It was added by CWG 1981.
After discussion of answers provided by Jerry Coffin and cpplearner, let me propose to rewrite those damn rules, like this:
[expr.const] 5.20\3 (modified)
An integral constant expression is an expression of integral or unscoped enumeration type, that is implicitly converted to a prvalue core constant expression of the same type such that the implicit conversion sequence contains only an lvalue-to-rvalue conversion.
[expr.const] 5.20\4 (modified)
A converted constant expression of type T is an expression of any
type, that is implicitly converted to a constant expression of type T
such that the implicit conversion sequence contains only:
user-defined conversions,
lvalue-to-rvalue conversions,
array-to-pointer conversions,
function-to-pointer conversions,
qualification conversions,
integral promotions,
integral conversions other than narrowing conversions,
null pointer conversions from std::nullptr_t,
null member pointer conversions from std::nullptr_t, and
function pointer conversions,
and where the reference binding (if any) binds directly. [ Note: such
expressions may be used in new expressions, as case expressions, as
enumerator initializers if the underlying type is fixed, as array
bounds, and as non-type template arguments. — end note ]
Now the difference is obvious, uh? Also it should be reminded that according to 5.20\7; 4\5 an expression of literal class type may be used instead of integral constant expression in some cases.

How implicit conversion works for non-type template parameters?

I guess (certain) implicit conversions apply when passing non-type template parameters. For example, there should be a conversion from int to std::size_t for expressions like std::array<int, 7>. However, consider the following code:
template <bool>
void f() {
std::cout << "false\n";
}
template <>
void f<true>() {
std::cout << "true\n";
}
int main() {
f<1>();
f<4>();
f<0>();
}
I expect int to be implicitly converted to bool here. But the behaviors are different on VC, GCC, and clang.
On VC, true, false, and false are printed, which is really weird to me.
On GCC, true, true, and false are printed, which is what I expect.
While on clang, the code does not compile at all due to the statement f<4>();.
candidate template ignored: invalid explicitly-specified argument for 1st template parameter
So, what does the standard say about this? What is the implicit conversion rule for non-type template parameters?
From the standard (§14.3.2/5):
The following conversions are performed on each expression used as a non-type template-argument. If a
non-type template-argument cannot be converted to the type of the corresponding template-parameter then
the program is ill-formed.
For a non-type template-parameter of integral or enumeration type, conversions permitted in a converted constant expression (5.19) are applied.
In §5.19, we learn (emphasis mine):
An integral constant expression is an expression of integral or unscoped enumeration type, implicitly converted
to a prvalue, where the converted expression is a core constant expression. ... A converted constant expression of
type T is an expression, implicitly converted to a prvalue of type T, where the converted expression is a core
constant expression and the implicit conversion sequence contains only user-defined conversions, lvalue-to-rvalue
conversions (4.1), integral promotions (4.5), and integral conversions (4.7) other than narrowing conversions
(8.5.4). [ Note: such expressions may be used in new expressions (5.3.4), as case expressions (6.4.2),
as enumerator initializers if the underlying type is fixed (7.2), as array bounds (8.3.4), and as integral or
enumeration non-type template arguments (14.3). —end note ]
So narrowing conversions (like converting 4 to bool) are explicitly disallowed for integral constant expressions, which are required in this case as a non-type template argument. That makes the call f<4>() ill-formed.
I believe Clang is correct in issuing an error, and GCC and VC are both nonconforming for not issuing any diagnostic.
[Update] This is GCC Bug #57891, looks like it's currently unassigned.

Calling constexpr in default template argument

In C++11 I am using a constexpr function as a default value for a template parameter - it looks like this:
template <int value>
struct bar
{
static constexpr int get()
{
return value;
}
};
template <typename A, int value = A::get()>
struct foo
{
};
int main()
{
typedef foo<bar<0>> type;
return 0;
}
G++ 4.5 and 4.7 compiles this, but Clang++ 3.1 does not. The error message from clang is:
clang_test.cpp:10:35: error: non-type template argument is not a constant expression
template <typename A, int value = A::get()>
^~~~~~~~
clang_test.cpp:17:19: note: while checking a default template argument used here
typedef foo<bar<3>> type;
~~~~~~~~~^~
clang_test.cpp:10:35: note: undefined function 'get' cannot be used in a constant expression
template <typename A, int value = A::get()>
^
clang_test.cpp:4:23: note: declared here
static constexpr int get()
^
1 error generated.
Which one is correct?
Richard Smith (zygoloid) at the LLVM IRC channel had a short talk with me about this issue which is your answer
<litb> hello folks
<litb> zygoloid, what should happen in this case?
<litb> http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument
<litb> it seems to be clang's behavior is surprising
<litb> zygoloid, i cannot apply the "point of instantiation" rule to constexpr
function templates. if i call such a function template, the called definition's
POI often is *after* the specialization reference, which means at the point of
the call, the constexpr function template specialization is "undefined".
<zygoloid> it's a horrible mess. Clang does not do what the standard intends, but
as you note, the actual spec is gloriously unclear
<d0k> :(
<zygoloid> we should instantiate bar<3>::get(), because it is odr-used, but we
don't, because we incorrectly believe it's used in an unevaluated context
<zygoloid> conversely, the point of instantiation is too late :/
<zygoloid> PR11851
So it seems that sometimes, Clang instantiates called function templates or member function of class templates but their instantiation is too late for the call to see, and at other cases it doesn't even instantiate them because it thinks it will never need them (unevaluated context).
I think GCC Clang is correct
quoted from n3290:
14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type, a converted > constant expression (5.19) of the type of the template-parameter; or
...
EDIT: 5.19 3
A literal constant expression is a prvalue core constant expression of
literal type, but not pointer type. An integral constant expression is
a literal constant expression of integral or unscoped enumeration
type. [ Note: Such expressions may be used as array bounds (8.3.4,
5.3.4), as bit-field lengths (9.6), as enumerator initializers if the underlying type is not fixed (7.2), as null pointer constants (4.10),
and as alignments (7.6.2). —end note ] A converted constant expression
of type T is a literal constant expression, implicitly converted to
type T, where the implicit conversion (if any) is permitted in a
literal constant expression and the implicit conversion sequence
contains only user-defined conversions, lvalue-to-rvalue conversions
(4.1), integral promotions (4.5), and integral conversions (4.7) other
than narrowing conversions (8.5.4).
[ Note: such expressions may be used as case expressions (6.4.2), as enumerator initializers if the underlying type is fixed (7.2), and as integral or enumeration non-type template arguments (14.3). —end note ]