Topic: the nature of constexpr functions
Author: restor <akrzemi1@gmail.com>
Date: Thu, 17 Jun 2010 11:59:17 CST Raw View
Hi,
constexpr functions have a nice feature of providing a uniform syntax
for performing some computations at both compilation time and program
execution time.
On the other hand, constexpr functions have an inconvenience of
providing a uniform syntax for performing some computations at both
compilation time and program execution time.
'pure' run-time function (normal functions) report errors as follows:
int factorial( int i ) {
if( i < 0 || i > 10 ) throw InvalidArgument();
...
}
'pure' compile-time meta functions have even nicer feature of
signalling errors during compilation:
template< int I >
struct Factorial {
static_assert( i >= 0 && i <= 10, "Invalid argument" );
...
}
Constexpr function, in turn cannot use either the former (because the
function may be called at compilation time), or the latter (because it
may be called at run-time). In the end it turns out that constexpr
function cannot report errors at all.
Is it a problem or not? One can argue that I used to type:
static const int daysInYear = 52 * 7 + 3;
and never complained that the expression (52 * 7 + 3) was incapable of
reporting errors. Why would I mind constexpr function having the same
behavior, since even its name suggests that this is but an expression.
On the other hand, I am often taught that the function should document
and possibly check its preconditions. If we had, one day, contract
programming in C++, we could say that:
constexpr int factorial( int i ) {
precondition( i >= 0 && i <= 10 );
return ...
}
Provides run-time contract verification if the function is called at
run-time, and is substituted for static_assert if the function is
evaluated during compilation.
Apart from the above, there is another point to be made: declaring the
function constexpr guarantees that it will never throw an exception,
and thus any invocation of such function with literal types
as arguments (at run-time), can be safely reported as never-throwing
by the noexcept-expression.
Regards,
&rzej
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "peter miller" <fuchsia.groan@virgin.net>
Date: Tue, 22 Jun 2010 16:41:51 CST Raw View
On Thu, 17 Jun 2010 18:59:17 +0100, restor <akrzemi1@gmail.com> wrote:
Hi &rzej
I'm no expert, but I think your point--while "on the money"--is wrong
in a few details:
[...]
> On the other hand, I am often taught that the function should document
> and possibly check its preconditions. If we had, one day, contract
> programming in C++, we could say that:
>
> constexpr int factorial( int i ) {
> precondition( i >= 0 && i <= 10 );
> return ...
> }
>
1) Your function isn't a constexpr function, because its body
must be of the form "{ return expression }"
Provides run-time contract verification if the function is called at
> run-time, and is substituted for static_assert if the function is
> evaluated during compilation.
>
2) the argument to a constexpr function isn't a constant expression,
otherwise the following would be legal:
template <int i> struct meta_func;
constexpr int myfunc( int i ) { return meta_func<i>::value; }
which means static_assert can't be used - as its argument must be a
constant expression. (I realise you didn't say static_assert could be
literally used, I'm just clarifying the issues here.)
Apart from the above, there is another point to be made: declaring the
> function constexpr guarantees that it will never throw an exception,
> and thus any invocation of such function with literal types
> as arguments (at run-time), can be safely reported as never-throwing
> by the noexcept-expression.
>
>
3) I think you *can* use exceptions in a constexpr function because
(according to 5.19 p2) unevaluated conditions don't stop a function
being constexpr, so the following IS legal, I believe:
constexpr int factorial( int i )
{
return ( i >= 1 && i <= 10 )
?
( i - 1 ? i * factorial( i - 1 ) : 1 )
:
throw std::domain_error( "factorial range is 1..10" );
}
This function can then be used at compile-time to produce constants
_when_its_argument_is_in_range_:
enum
{
n10 = factorial( 10 ), // compiles n10 = 3628800
n11 = factorial( 11 ), // compile-time error:
// not a constant expression.
};
And it can obvioulsy be used in a runtime context, throwing an error
if its argument is out of range:
int main()
{
cout << factorial( 10 ) // compiles and executes
<< ','
<< factorial( 11 ); // compiles, but throws
}
I guess that's why constexpr functions don't report noexcept
(I haven't looked at that.)
So I think you are right: constexpr has added a whole class of
opaque errors that will fox beginners and experts alike
("??!Why is 10 a constant expression but not 11?!!"). And it
would be helpful if there was a way to get sensible error
messages out.
Peter
--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =3D?ISO-8859-1?Q?Daniel_Kr=3DFCgler?=3D <daniel.kruegler@googlemail.c=.om>
Date: Wed, 23 Jun 2010 13:12:43 CST Raw View
On Jun 23, 12:41 am, "peter miller" <fuchsia.gr...@virgin.net> wrote:
> On Thu, 17 Jun 2010 18:59:17 +0100, restor <akrze...@gmail.com> wrote:
[..]
> > On the other hand, I am often taught that the function should document
> > and possibly check its preconditions. If we had, one day, contract
> > programming in C++, we could say that:
>
> > constexpr int factorial( int i ) {
> > precondition( i >= 0 && i <= 10 );
> > return ...
> > }
>
> 1) Your function isn't a constexpr function, because its body
> must be of the form "{ return expression }"
By the current wording this observation is correct, even though
it a very unnecessary restriction to forbid static_assertions,
typedef's, or the definition of local constants within the body, e.g.
constexpr int factorial( int i ) {
typedef int IT; // Not allowed - why?
const IT zero = 0; // Not allowed - why?
static_assert( i >= zero && i <= 10 ); // Not allowed - why?
return ...
}
of-course we have a workaround for such restrictions and
we can delegate to a helper class for these things - but it
has the effect of an unnecessary namespace pollution (we
cannot even define a local class within the constexpr
function).
> Provides run-time contract verification if the function is called at
>
> > run-time, and is substituted for static_assert if the function is
> > evaluated during compilation.
>
> 2) the argument to a constexpr function isn't a constant expression,
> otherwise the following would be legal:
>
> template <int i> struct meta_func;
> constexpr int myfunc( int i ) { return meta_func<i>::value; }
>
> which means static_assert can't be used - as its argument must be a
> constant expression. (I realise you didn't say static_assert could be
> literally used, I'm just clarifying the issues here.)
Your implication is wrong. There is intentionally no restriction on
constant expression constexpr function arguments. Of-course in
this case, the usage of myfunc would be ill-formed with arguments
that are no constant expressions. Here is an absolutely legal
usage of another constexpr function:
constexpr int seconds_per_hour(int h) {
return 3600 * h;
}
constexpr int c = seconds_per_hour(5); // OK
int get();
int v = seconds_per_hour(get()); // also OK, but runtime
int main() {
int hours = 0;
std::cin >> hours;
std:.cout << seconds_per_hour(hours) << std::endl; // OK, but
runtime
}
> Apart from the above, there is another point to be made: declaring the
> function constexpr guarantees that it will never throw an exception,
> > and thus any invocation of such function with literal types
> > as arguments (at run-time), can be safely reported as never-throwing
> > by the noexcept-expression.
>
> 3) I think you *can* use exceptions in a constexpr function because
> (according to 5.19 p2) unevaluated conditions don't stop a function
> being constexpr, so the following IS legal, I believe:
>
> constexpr int factorial( int i )
> {
> return ( i >= 1 && i <= 10 )
> ?
> ( i - 1 ? i * factorial( i - 1 ) : 1 )
> :
> throw std::domain_error( "factorial range is 1..10" );
> }
While this is correct, I would clearly prefer a static_assertion
to signal the failure (e.g. wrapped in a helper class as of the
current restrictions). The code above has the misleading effect,
that the uninitiated reader may believe that you could potentially
evaluate a throw-expression within a constexpr function - this
is not possible as per 5.19/2. So, above definition has the same
restriction as an alternative code using constant expression
verifications: You could only provide constant expression
arguments for this special constexpr function definition, even
though this restriction does not generally exist.
> This function can then be used at compile-time to produce constants
> _when_its_argument_is_in_range_:
>
> enum
> {
> n10 = factorial( 10 ), // compiles n10 = 3628800
> n11 = factorial( 11 ), // compile-time error:
> // not a constant expression.
>
> };
This is correct.
> And it can obvioulsy be used in a runtime context, throwing an error
> if its argument is out of range:
>
> int main()
> {
> cout << factorial( 10 ) // compiles and executes
> << ','
> << factorial( 11 ); // compiles, but throws
>
> }
This code is ill-formed, because the existence of the throw-expression
in an potentially evaluated sub-expression as described in 5.19/2
is not supported within constexpr functions.
> I guess that's why constexpr functions don't report noexcept
No, this is not the reason. It would perfectly make sense to
ensure that constexpr functions are implicitly noexcept functions.
> So I think you are right: constexpr has added a whole class of
> opaque errors that will fox beginners and experts alike
> ("??!Why is 10 a constant expression but not 11?!!"). And it
> would be helpful if there was a way to get sensible error
> messages out.
I don't think that a careful programmer would really use
a throw-expression within a constexpr function. It would
be much more helpful, if static assertions would be
explicitly allowed within constexpr function because
they are always constant expressions.
HTH & Greetings from Bremen,
Daniel Kr=FCgler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: restor <akrzemi1@gmail.com>
Date: Wed, 23 Jun 2010 17:19:30 CST Raw View
> 3) I think you *can* use exceptions in a constexpr function because
> (according to 5.19 p2) unevaluated conditions don't stop a function
> being constexpr, so the following IS legal, I believe:
>
> constexpr int factorial( int i )
> {
> return ( i >= 1 && i <= 10 )
> ?
> ( i - 1 ? i * factorial( i - 1 ) : 1 )
> :
> throw std::domain_error( "factorial range is 1..10" );
>
> }
> So I think you are right: constexpr has added a whole class of
> opaque errors that will fox beginners and experts alike
> ("??!Why is 10 a constant expression but not 11?!!"). And it
> would be helpful if there was a way to get sensible error
> messages out.
Hi. Now that you pointed that out, I believe that I was totally
wrong.
It looks to me like constexpr functions provide a very nice way of
reporting errors: normal throw at run-time and compilation error when
called at compile-time.
The compile-time error is un-informative, but an uninformative compile-
time error is often better than the informative run-time one. This
last claim, which I tend to agree with, could be argued, but it must
be admitted that it has some merit.
Regards,
&rzej
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: restor <akrzemi1@gmail.com>
Date: Thu, 24 Jun 2010 12:22:28 CST Raw View
> > 3) I think you *can* use exceptions in a constexpr function because
> > (according to 5.19 p2) unevaluated conditions don't stop a function
> > being constexpr, so the following IS legal, I believe:
>
> > constexpr int factorial( int i )
> > {
> > return ( i >= 1 && i <= 10 )
> > ?
> > ( i - 1 ? i * factorial( i - 1 ) : 1 )
> > :
> > throw std::domain_error( "factorial range is 1..10" );
> > }
>
> While this is correct, I would clearly prefer a static_assertion
> to signal the failure (e.g. wrapped in a helper class as of the
> current restrictions). The code above has the misleading effect,
> that the uninitiated reader may believe that you could potentially
> evaluate a throw-expression within a constexpr function - this
> is not possible as per 5.19/2. So, above definition has the same
> restriction as an alternative code using constant expression
> verifications: You could only provide constant expression
> arguments for this special constexpr function definition, even
> though this restriction does not generally exist.
> > enum
> > {
> > n10 = factorial( 10 ), // compiles n10 = 3628800
> > n11 = factorial( 11 ), // compile-time error:
> > // not a constant expression.
>
> > };
>
> This is correct.
> > int main()
> > {
> > cout << factorial( 10 ) // compiles and executes
> > << ','
> > << factorial( 11 ); // compiles, but throws
>
> > }
>
> This code is ill-formed, because the existence of the throw-expression
> in an potentially evaluated sub-expression as described in 5.19/2
> is not supported within constexpr functions.
Oh, this is really strange. This means I can use (or abuse) constexpr
functions in the following ways:
1. This function is correct; it compiles fine - 2nd and 3rd arg are
not evaluated; but can never be called. (Its adress can still be
taken?)
constexpr int fun1( bool cond )
{
return cond ? throw Ex() : throw Ex();
}
2. This function cis correct; but it cannot be called at runtime. At
compiletime it can only be called with cond == true (a sort of
static_assert).
constexpr int fun2( bool cond )
{
return cond ? 0 : throw Ex();
}
3. This function is a "compile-time-only" function: it cannot be
called at runtime and can be called at compile time with any value,
and in any case it will do the same.
constexpr int fun3( bool cond )
{
return cond ? fun_impl() : ( !cond ? fun_impl() : throw Ex() );
}
Of course expression "throw Ex()" can be replaced with any other
purely "runtime" expression. Are the above examples correct?
Regards,
&rzej
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: restor <akrzemi1@gmail.com>
Date: Thu, 24 Jun 2010 12:24:55 CST Raw View
> By the current wording this observation is correct, even though
> it a very unnecessary restriction to forbid static_assertions,
> typedef's, or the definition of local constants within the body, e.g.
>
> constexpr int factorial( int i ) {
> typedef int IT; // Not allowed - why?
> const IT zero = 0; // Not allowed - why?
> static_assert( i >= zero && i <= 10 ); // Not allowed - why?
> return ...
>
> }
Hi,
What would be the meaning of static_assert in a constexpr function if
it
was called in a non-constexpr context?
constexpr int factorial( int i ) {
static_assert( i >= 0 && i <= 10 );
return ...
}
int main() {
int i;
std::cin >> i;
factorial(i); // what should static_assert do?
}
Should it be ignored? Or somehow changed into some run-time check?
Regards,
&rzej
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =3D?ISO-8859-1?Q?Daniel_Kr=3DFCgler?=3D <daniel.kruegler@googlemail.c=.om>
Date: Thu, 24 Jun 2010 18:30:33 CST Raw View
On 24 Jun., 20:22, restor <akrze...@gmail.com> wrote:
[..]
> Oh, this is really strange. This means I can use (or abuse) constexpr
> functions in the following ways:
>
> 1. This function is correct; it compiles fine - 2nd and 3rd arg are
> not evaluated; but can never be called. (Its adress can still be
> taken?)
>
> constexpr int fun1( bool cond )
> {
> return cond ? throw Ex() : throw Ex();
> }
This function definition should be ill-formed, because it
violates 5.19 [expr.const]/2:
"A conditional-expression is a constant expression unless it involves
one of the following as a potentially evaluated subexpression (3.2),
but subexpressions of logical AND (5.14), logical OR (5.15), and
conditional (5.16) operations that are not evaluated are not
considered"
but 7.1.5/5 requires:
"The definition of a constexpr function shall satisfy the following
constraints:
[..]
=97 its function-body shall be a compound-statement of the form
{ return expression ; }
where expression is a **potential constant** expression (5.19)
[..]"
(See also the next bullet, why I was wrong in my previous reply)
> 2. This function cis correct; but it cannot be called at runtime. At
> compiletime it can only be called with cond == true (a sort of
> static_assert).
>
> constexpr int fun2( bool cond )
> {
> return cond ? 0 : throw Ex();
> }
Sorry, I was wrong in my previous reply: The requirements for a
constexpr
function say that the *complete* return expression must be a
potentially
constant expression, which is not true for above function.
> 3. This function is a "compile-time-only" function: it cannot be
> called at runtime and can be called at compile time with any value,
> and in any case it will do the same.
>
> constexpr int fun3( bool cond )
> {
> return cond ? fun_impl() : ( !cond ? fun_impl() : throw Ex() );
> }
>
> Of course expression "throw Ex()" can be replaced with any other
> purely "runtime" expression. Are the above examples correct?
Same reason as before: This function definition is ill-formed.
I need to correct myself a second time (sigh!):
The rules for constexpr function templates are extreme relaxed
in regard to the usefulness of the constexpr specifier: As of 7.1.5/5:
"If the instantiated template specialization of a constexpr function
template would fail to satisfy the requirements for a constexpr
function or constexpr constructor, the constexpr specifier is
ignored."
This means that I could define
template<class T>
constexpr void f() {} // OK
int main() {
f<bool>(); // OK, but not constexpr (of-course)
}
but this function could never be constexpr (due to the return type
void). Personally, I consider this as a defect. IMO only
value-dependent and type-dependent expressions within such
a function definition should make an instantiated constexpr
function template make well-formed if violating the constexpr
requirements. Currently it just says that constexpr is ignored,
which looks like a great source of silent programmer errors to
me.
This also means that your template examples are all well-formed,
but those with any expression that is not a potential constant
expression will not be a constexpr function.
Sorry for the misleading lapsus in my first response &&
Greetings from Bremen,
Daniel Kr=FCgler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =3D?ISO-8859-1?Q?Daniel_Kr=3DFCgler?=3D <daniel.kruegler@googlemail.c=.om>
Date: Thu, 24 Jun 2010 18:30:28 CST Raw View
On 24 Jun., 20:24, restor <akrze...@gmail.com> wrote:
> > By the current wording this observation is correct, even though
> > it a very unnecessary restriction to forbid static_assertions,
> > typedef's, or the definition of local constants within the body, e.g.
>
> > constexpr int factorial( int i ) {
> > typedef int IT; // Not allowed - why?
> > const IT zero = 0; // Not allowed - why?
> > static_assert( i >= zero && i <= 10 ); // Not allowed - why?
> > return ...
>
> > }
>
> Hi,
> What would be the meaning of static_assert in a constexpr function if
> it
> was called in a non-constexpr context?
>
> constexpr int factorial( int i ) {
> static_assert( i >= 0 && i <= 10 );
> return ...
> }
>
> int main() {
> int i;
> std::cin >> i;
> factorial(i); // what should static_assert do?
> }
>
> Should it be ignored? Or somehow changed into some run-time check?
Neither of the latter choices. It would just make the
code ill-formed, if the actual arguments don't end up
that the whole expression is a constant expression.
Same thing as if you would use a class template
instead:
template<bool OK>
struct static_asserter {
static_assert( OK, "Not OK");
constexpr static_asserter() {}
};
constexpr int factorial( int i ) {
return (static_asserter< (i >= 0 && i <= 10)>(), ...);
}
HTH & Greetings from Bremen,
Daniel Kr=FCgler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: restor <akrzemi1@gmail.com>
Date: Fri, 25 Jun 2010 04:13:12 CST Raw View
> > What would be the meaning of static_assert in a constexpr function if
> > it
> > was called in a non-constexpr context?
>
> > constexpr int factorial( int i ) {
> > static_assert( i >= 0 && i <= 10 );
> > return ...
> > }
>
> > int main() {
> > int i;
> > std::cin >> i;
> > factorial(i); // what should static_assert do?
> > }
>
> > Should it be ignored? Or somehow changed into some run-time check?
>
> Neither of the latter choices. It would just make the
> code ill-formed, if the actual arguments don't end up
> that the whole expression is a constant expression.
> Same thing as if you would use a class template
> instead:
>
> template<bool OK>
> struct static_asserter {
> static_assert( OK, "Not OK");
> constexpr static_asserter() {}
>
> };
>
> constexpr int factorial( int i ) {
> return (static_asserter< (i >= 0 && i <= 10)>(), ...);
>
> }
I am still a bit confused. Is your example above (that uses a helper
struct template static_asserter) valid in current FCD? Can you use a
formal parameter of a constexpr function as an actual argument to a
template?
Regards,
&rzej
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Sat, 26 Jun 2010 14:08:10 CST Raw View
On Jun 25, 2:30 am, =?ISO-8859-1?Q?Daniel_Kr=FCgler?=
<daniel.krueg...@googlemail.c=.om> wrote:
> On 24 Jun., 20:24, restor <akrze...@gmail.com> wrote:
> > What would be the meaning of static_assert in a constexpr function if
> > it was called in a non-constexpr context?
>
> > constexpr int factorial( int i ) {
> > static_assert( i >= 0 && i <= 10 );
> > return ...
> > }
>
> > int main() {
> > int i;
> > std::cin >> i;
> > factorial(i); // what should static_assert do?
> > }
>
> > Should it be ignored? Or somehow changed into some run-time check?
>
> Neither of the latter choices. It would just make the
> code ill-formed, if the actual arguments don't end up
> that the whole expression is a constant expression.
> Same thing as if you would use a class template
> instead:
>
> template<bool OK>
> struct static_asserter {
> static_assert( OK, "Not OK");
> constexpr static_asserter() {}
> };
>
> constexpr int factorial( int i ) {
> return (static_asserter< (i >= 0 && i <= 10)>(), ...);
> }
Mea culpa, I need to correct myself *again*:
You are right that the function parameters of any
constexpr function are "potentially non-constant
expressions", therefore neither my static_assertion
example nor my transformed class template function
will be well-formed with in the constexpr function.
In summary, this means:
1) Every constexpr function could be automatically
a noexcept function, because we cannot have a
direct or indirect throw-expression within a
constexpr function[1]
2) We cannot use the function parameters of a constexpr
function as part of expressions that require a constant
expression, e.g. we cannot use them as arguments of
non-type templates.
[1]: Again, according to the current wording you can have
such an expression within a constexpr function template,
but upon instantiation it will not be a constexpr function,
e.g.
template<class>
constexpr bool foo() { return (throw 0, false); }
is supposed to be well-formed, but not a constexpr function
for any type for which it is instantiated.
I hope, that after this n-th order correction the situation
becomes a bit clearer ;-)
Back to your original question:
a) It is correct, that we cannot use any form of parameter validation
inside a constexpr function, nor can we perform any "runtime"
validation based on non-constant expressions.
b) Nevertheless it is possible, to verify all constant expressions
inside the implementation of a constexpr function by static_assert's
(hidden in a helper class template). E.g. your original example
involving the days for a year could be checked against the expected
number of seconds of a year or similar tests.
Greetings from Bremen,
Daniel Kr gler
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Nikolay Ivchenkov <tsoae@mail.ru>
Date: Sat, 26 Jun 2010 19:19:21 CST Raw View
On 23 Jun, 23:12, Daniel Krugler wrote:
> > 2) the argument to a constexpr function isn't a constant expression,
> > otherwise the following would be legal:
>
> > template <int i> struct meta_func;
> > constexpr int myfunc( int i ) { return meta_func<i>::value; }
>
> > which means static_assert can't be used - as its argument must be a
> > constant expression. (I realise you didn't say static_assert could be
> > literally used, I'm just clarifying the issues here.)
>
> Your implication is wrong. There is intentionally no restriction on
> constant expression constexpr function arguments. Of-course in
> this case, the usage of myfunc would be ill-formed with arguments
> that are no constant expressions.
I believe that this function definition is ill-formed.
template <int N>
struct X
{ static constexpr int value = N; };
constexpr int f(int n)
{
return X<n>::value;
}
According to N3092 - 5.19/2,
A conditional-expression is a constant expression unless it involves
one of the following as a potentially evaluated
subexpression (3.2) [...]:
-- an lvalue-to-rvalue conversion (4.1) unless it is applied to
---- a glvalue of integral or enumeration type that refers to a non-
volatile const object with a preceding
initialization, initialized with a constant expression, or
---- a glvalue of literal type that refers to a non-volatile object
defined with constexpr, or that refers
to a sub-object of such an object, or
---- a glvalue of literal type that refers to a non-volatile temporary
object initialized with a constant
expression;
In the example above the expression n is an glvalue of integral type
that refers to a non-const non-temporary object that is not defined
with constexpr. So, the expression n is not a constant expression,
while the template argument (for the template parameter N) must be a
constant expression (any potential constant expression is not
explicitly allowed in this context).
Note that unlike a parameter of a constexpr function, an invocation of
a constexpr function (under some circumstances) is explicitly
mentioned in the list of allowable constant expressions. Moreover, how
the same function can refer to different class template
specializations? Unlike a function template, a constexpr function is
not a family of functions and it can't have different instances.
#include <iostream>
template <bool>
struct A
{ static int x() { return 1; } };
template <>
struct A<true>
{ typedef int x; };
constexpr int f(bool b)
{
return sizeof(A<b>::x());
// what is A<b>::x():
// a function call or a functional type conversion?
}
int main()
{
std::cout << f(false) << std::endl;
std::cout << f(true) << std::endl;
}
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: restor <akrzemi1@gmail.com>
Date: Mon, 28 Jun 2010 14:01:57 CST Raw View
> Back to your original question:
>
> a) It is correct, that we cannot use any form of parameter validation
> inside a constexpr function, nor can we perform any "runtime"
> validation based on non-constant expressions.
>
> b) Nevertheless it is possible, to verify all constant expressions
> inside the implementation of a constexpr function by static_assert's
> (hidden in a helper class template). E.g. your original example
> involving the days for a year could be checked against the expected
> number of seconds of a year or similar tests.
Hi, Could you give me some hint how such a compiletime check could be
implemented in a constexpr function? I am really having trouble
imagining one.
The only thing that comes to my mind:
struct DeclaredButUndefinedUserType;
constexpr unsigned factorial( unsigned i )
{
return (i <= 10)
? i * factorial(i - 1)
: DeclaredButUndefinedUserType();
}
But I guess it will not work either.
Now, I am strting to think that doing such a check is impossible by
definition.
static_assert works by making some code ill-formed.
Potential Constant Expression, on the other hand, is defined as an
Expression that must become
Constant Expression for ANY value that we might wish to substitute (is
it not what 5.19/6 says?).
Adding a static_assert, means that for some arbitrary constant values
our Potential Constant Expression
is ill-formed, hence for those constant values, the Potential Constant
Expression is not a Constant Expression,
and thus cannot be a body of a constexpr function.
Would that be a correct reasoning?
Regards,
&rzej
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use
mailto:std-c++@netlab.cs.rpi.edu<std-c%2B%2B@netlab.cs.rpi.edu>
]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]