Re: Meaning of terms "subexpression" and "constant expression"
Nikolay Ivchenkov wrote:
Consider the following example:
#include <iostream>
template <void (*pf)()>
struct X
{
template <void (*)()>
struct Y;
static void instantiate() { (void)m; }
typedef Y<&X::instantiate> Inst;
X() { pf(); }
static X m;
};
template <void (*pf)()>
X<pf> X<pf>::m;
void f()
{
std::cout << "f()\n";
}
int main()
{
sizeof X<&f>();
}
According to N3225 - 3.2/2,
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof.
Can the expression f in sizeof X<&f>() be considered as subexpression
of X<&f>()? Is the expression f potentially evaluated and is the
function f odr-used?
I agree, this smells. I can't find what in the spec requires "f" to be
defined either.
Another example:
int main()
{
int m = 11;
int n = 22;
sizeof(new char[m]); // (1)
sizeof(new char[m][n]); // (2)
}
According to N3225 - 5.19/2,
A conditional-expression is a constant expression unless it involves
one of the following as a potentially evaluated subexpression (3.2)
[...]
Does this rule imply that
at (1) new char[m] and m are constant expressions,
at (2) new char[m][n], m, and even n are constant expressions?
I think "subexpression" here is stated in a compositional manner; the
expression 'm' is considered to be a subexpression of itself.
5.19/2 also contains the following wording:
an invocation of an undefined constexpr function or an undefined
constexpr constructor outside the definition of a constexpr function
or a constexpr constructor
This does not make sense to me. Should it say the following?
an invocation of an undefined constexpr function or an undefined
constexpr constructor outside of its own definition.
I have this feeling that I'm missing the obvious.
Where exactly a constexpr function shall be considered defined?
struct X
{
static constexpr int size()
{
return sizeof(X);
}
char arr[size()]; // is 'size' undefined here?
};
struct Y
{
static constexpr int offset()
{
return offsetof(Y, c);
}
char arr[offset()]; // is 'offset' undefined here?
char c;
};
offset/size is defined at that place. The body of member functions assume
that their class type is complete though, which will cause problems here.
Function invocation substitution is said to "not change the meaning". I
think that requires that "X" must still be considered a completely defined
class type even during function invocation substitution. I think this smells
too.
struct Z
{
static constexpr int f()
{ return g(); } // is 'g' defined here?
static constexpr int g()
{ return 0; }
};
I think g is considered defined there, as Z is considered a completely
defined class type. The call to "f()" should yield a constant expression, no
matter from where the call happens.
Is the following program well-formed?
constexpr int foo()
{
// is it "outside the definition of
// a constexpr function"?
return bar();
}
char arr[foo()];
constexpr int bar()
{
return 1;
}
int main() {}
This one is ill-formed. "bar" was not declared prior to the call. Assuming
that "bar" was declared prior to "foo" (but not defined yet), it's well-
formed if we read 5.19p2 literally. But I can't make sense of its literal
meaning. Maybe I'm missing something?
If the answer is "yes", the next question:
is the following program well-formed?
constexpr int foo()
{
return bar();
}
char arr[foo()];
constexpr int bar()
{
// cyclic dependency
// (but this is not a recursive function invocation)
return sizeof arr;
}
int main() {}
This is ill-formed too. "bar" was not defined at the point of the call. If
we assume it was declared prior to foo, it would be ill-formed/well-formed
for the same reason as the prior code.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]