struct Test
{
static const int value = []() -> int { return 0; } ();
};
With gcc-4.6 I get something like, error: function needs to be constexpr. I have tried multiple combinations of putting constexpr at various places, but no luck.
Is constexpr supported for lambda functions as well (irrespective of return type specified or not) ? What is the correct syntax ?
Any work around possible ?
Update: As of C++17, lambdas are permitted in constant expressions.
Lambdas are currently (C++14) not allowed in constant expressions as per [expr.const]/(2.6), but they will once N4487 is accepted (which can be found in the working draft N4582):
This proposal suggests allowing lambda-expressions in constant
expressions, removing an existing restriction. The authors propose
that certain lambda-expressions and operations on certain closure
objects be allowed to appear within constant expressions. In doing so,
we also propose that a closure type be considered a literal type if
the type of each of its data-members is a literal type; and, that if
the constexpr specifier is omitted within the lambda-declarator, that
the generated function call operator be constexpr if it would satisfy
the requirements of a constexpr function (similar to the
constexpr inference that already occurs for implicitly defined
constructors and the assignment operator functions).
From the C++0x FDIS §7.1.5[dcl.constexpr]/1:
The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type.
A lambda expression is none of those things and thus may not be declared constexpr.
Prior to C++17 lambdas are not compatible with constexpr. They cannot be used inside constant expressions.
Starting with C++17 lambdas are constexpr where it makes sense. The proposal N4487 will be put into the C++17 standard. On his website Herb Sutter, chair of the ISO C++ committee, stated the following:
Lambdas are now allowed inside constexpr functions.
FFWD to year 2018 :)
auto my_const_expression_lambda = []()
constexpr -> bool
{
return true ;
}
Since c++17
Related
It seems that the rules for the compile-time keywords: constexpr, consteval and constinit are defined well enough for compilers to warn if you misapply the label.
It would make sense that (much like inline) the compiler can, in all places, apply rules to determine if, in fact, code could have one of the compile-time keywords applied and, be forced, per language specification, to compile as much as possible as if the compile-time keywords had been applied.
Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied. Compile that function, as if all the functions called had the correct compile-time keywords.
Here is a simple example:
#include <tuple>
consteval std::tuple<int, int> init1(int x, int y)
{
return {x,y};
}
std::tuple<int, int>& foo1()
{
static constinit std::tuple<int, int> x=init1(1,2);
return x;
}
std::tuple<int, int> init2(int x, int y)
{
return {x,y};
}
std::tuple<int, int>& foo2()
{
static std::tuple<int, int> x=init2(1,2);
return x;
}
static std::tuple<int, int> x3=init2(1,2);
std::tuple<int, int>& foo3()
{
return x3;
}
see it here: https://godbolt.org/z/KrzGfnEo7
Note that init1/foo1 are fully specified with compile-time keywords and there is no need for a guard variable, since it is initialized completely (not just 0 filled).
The question is about why the compiler doesn't do the same in the cases of init2/foo2 or x3/foo3? Or more precisely why the compiler is allowed NOT to initialize fully at compile time.
EDIT (as per comment) see (Why do we need to mark functions as constexpr?) constexpr would be required. I am concerned more with cases of consteval and constinit. Could the specification be modified to require trying to resolve code at compile-time and not failing because of the absence of constexpr? This would allow interface contracts to just use constexpr as per the related question.
Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied.
The basis of your question is the assumption that these keywords are just variations on a theme, that a function which could have some of them ought to have a specific one based on the properties within the function alone.
That's not reasonable.
For functions, any function which could be constexpr could also be consteval. The difference is that one can be called during constant expression evaluation, and the other must be called only during constant expression evaluation. This is not an intrinsic property of the function definition; it is about how the function is to be used.
The compiler should not override the decision of a programmer to be able to use a function at runtime just because the function definition just so happens to also be legit for consteval.
It should also be noted that there is the requirements of a function definition for a consteval function are the same as for a constexpr function.
It is also not possible to know if a function definition ought to be constexpr or not. Remember: while a constexpr function explicitly forbids certain constructs from appearing in the definition, it also permits you to have certain constructs in the definition that aren't allowed to be evaluated during constant evaluation... so long as you never actually allow those code paths to be evaluated during constant evaluation. So there are many function definitions that just so happen to be valid for constexpr even if the programmer didn't intend for them to be evaluated at compile-time.
Lambdas get implicit constexpr definitions only because space in a lambda expression is at a premium.
For variables, implicit constexpr makes even less sense. A variable ought to be constexpr if you intend to use it in a constant expression later on. This requires it to have an initializer that is a constant expression. Implicit constexpr is a problem because you might start relying on the implicit constexpr rule, then change the initializer to no longer be a constant expression and get a strange error in the place where you used its constexpr properties.
It's better to make the user be explicit up-front than to misjudge what the user was trying to do and make a user think something is supposed to be valid when it wasn't intended.
And constinit is solely about ensuring that a user never accidentally uses a non-constant expression to initialize the variable. Making it implicit would make absolutely no sense, as changing the expression to no longer be a constant expression wouldn't cause compilation to fail. Compilers are smart enough to know when a variable is initialized with a constant expression and can decide how to handle that on its own.
The following snippet compiles with no error with Clang 4.0 but GCC 7.0 produces errors (note the use of -std=c++1z flag).
using FuncT = int (*)(double);
template <FuncT FUNC>
int temp_foo(double a)
{
return FUNC(a);
}
int foo(double a)
{
return 42;
}
void func()
{
auto lambda = [](double a) { return 5; };
struct MyStruct
{
static int foo(double a) { return 42; }
};
temp_foo<foo>(3);
temp_foo<static_cast<FuncT>(lambda)>(3);
temp_foo<MyStruct::foo>(3);
}
Specifically, GCC complains that both the lambda and the nested class's method have no linkage, so they can't be used as a non-type template argument.
At least for the lambda case I think that Clang is correct (and GCC is wrong) since (quoting from cppreference, the conversion operator):
The value returned by this conversion function is a pointer to a
function with C++ language linkage that, when invoked, has the same
effect as invoking the closure object's function call operator
directly.
Is GCC misbehaving?
According to http://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter, it seems like external linkage is no longer a requirement since C++17. The same language is found in the C++17 draft under [temp.arg.nontype] at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf (note that it is incorrectly linked as a C++14 draft).
The template argument that can be used with a non-type template parameter can be any converted constant expression of the type of the template parameter...
The only exceptions are that non-type template parameters of reference and pointer type cannot refer to/be the address of
a subobject (including non-static class member, base subobject, or array
element);
a temporary object (including one created during reference initialization);
a string literal;
the result of typeid;
or the predefined variable __func__.
That link on cppreference also specifically mentions function pointers, pre C++ 17:
The following limitations apply when instantiating templates that have non-type template parameters:
...
For pointers to functions, the valid arguments are pointers to functions with linkage (or constant expressions that evaluate to null pointer values).
Since your question is labelled C++1z (we should probably have a 17 tag by now and use that instead since 17 is finished) we should use the first set of rules. Your example does not seem to fall into any of the exception categories for C++ 17, and therefore gcc is in error.
Note that clang does not compile your example if you change the language flag to 14.
I agree with Nir's answer and wanted to add some information to it. He cites the relevant section in the standard (§14.3.2 [temp.arg.nontype]) that shows that there is no longer a requirement for non-type parameters to have linkage, but that still doesn't show that GCC is misbehaving for the lambda part. For that we need to show that static_cast<FUNCT>(lambda) is a converted constant expression. For that we need a newer draft from the one Nir linked. And this section
§5.1.5 Lambda expressions [expr.prim.lambda]:
The closure type for a non-generic lambda-expression with no lambda-capture has a conversion function to pointer to function with
C++ language linkage (7.5) having the same parameter and return types
as the closure type’s function call operator. [...] The conversion
function [...] is public, constexpr,
non-virtual, non-explicit, const, and has a non-throwing exception
specification.
Interestingly enough, GCC claims to have already implemented this (N4268) in the already released version 6 (in case you want to excuse GCC's behavior by saying that GCC 7 hasn't been officially released yet, so maybe when it will come out this will be fixed):
Language Feature Proposal Available in GCC? SD-6 Feature Test
Allow constant evaluation for all non-type template arguments N4268 6 __cpp_nontype_template_args >= 201411
So in summary, this is a bug in GCC.
struct Test
{
static const int value = []() -> int { return 0; } ();
};
With gcc-4.6 I get something like, error: function needs to be constexpr. I have tried multiple combinations of putting constexpr at various places, but no luck.
Is constexpr supported for lambda functions as well (irrespective of return type specified or not) ? What is the correct syntax ?
Any work around possible ?
Update: As of C++17, lambdas are permitted in constant expressions.
Lambdas are currently (C++14) not allowed in constant expressions as per [expr.const]/(2.6), but they will once N4487 is accepted (which can be found in the working draft N4582):
This proposal suggests allowing lambda-expressions in constant
expressions, removing an existing restriction. The authors propose
that certain lambda-expressions and operations on certain closure
objects be allowed to appear within constant expressions. In doing so,
we also propose that a closure type be considered a literal type if
the type of each of its data-members is a literal type; and, that if
the constexpr specifier is omitted within the lambda-declarator, that
the generated function call operator be constexpr if it would satisfy
the requirements of a constexpr function (similar to the
constexpr inference that already occurs for implicitly defined
constructors and the assignment operator functions).
From the C++0x FDIS §7.1.5[dcl.constexpr]/1:
The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type.
A lambda expression is none of those things and thus may not be declared constexpr.
Prior to C++17 lambdas are not compatible with constexpr. They cannot be used inside constant expressions.
Starting with C++17 lambdas are constexpr where it makes sense. The proposal N4487 will be put into the C++17 standard. On his website Herb Sutter, chair of the ISO C++ committee, stated the following:
Lambdas are now allowed inside constexpr functions.
FFWD to year 2018 :)
auto my_const_expression_lambda = []()
constexpr -> bool
{
return true ;
}
Since c++17
struct Test
{
static const int value = []() -> int { return 0; } ();
};
With gcc-4.6 I get something like, error: function needs to be constexpr. I have tried multiple combinations of putting constexpr at various places, but no luck.
Is constexpr supported for lambda functions as well (irrespective of return type specified or not) ? What is the correct syntax ?
Any work around possible ?
Update: As of C++17, lambdas are permitted in constant expressions.
Lambdas are currently (C++14) not allowed in constant expressions as per [expr.const]/(2.6), but they will once N4487 is accepted (which can be found in the working draft N4582):
This proposal suggests allowing lambda-expressions in constant
expressions, removing an existing restriction. The authors propose
that certain lambda-expressions and operations on certain closure
objects be allowed to appear within constant expressions. In doing so,
we also propose that a closure type be considered a literal type if
the type of each of its data-members is a literal type; and, that if
the constexpr specifier is omitted within the lambda-declarator, that
the generated function call operator be constexpr if it would satisfy
the requirements of a constexpr function (similar to the
constexpr inference that already occurs for implicitly defined
constructors and the assignment operator functions).
From the C++0x FDIS §7.1.5[dcl.constexpr]/1:
The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type.
A lambda expression is none of those things and thus may not be declared constexpr.
Prior to C++17 lambdas are not compatible with constexpr. They cannot be used inside constant expressions.
Starting with C++17 lambdas are constexpr where it makes sense. The proposal N4487 will be put into the C++17 standard. On his website Herb Sutter, chair of the ISO C++ committee, stated the following:
Lambdas are now allowed inside constexpr functions.
FFWD to year 2018 :)
auto my_const_expression_lambda = []()
constexpr -> bool
{
return true ;
}
Since c++17
I understand that using templates is one of the appreciated way of overloading but i was wondering why auto cannot be used for function parameter type deduction hence aiding overloading of the function?
N3690 says in 7.6.1.4/3 that lambda expression can be made generic using auto,providing this example
auto glambda = [](int i, auto a) { return i; };//OK: a generic lambda
(note: this is not mentioned in N3485)
1).why cant i do a similar thing for a normal function for e.g
void swap(auto& param1, decltype(param1)& param2)
{
decltype(param1) temp = param1;
param1 = param2;
param2 = temp;
}
this gives errors error : parameters declared auto.
from N3690 7.1.6.4/4
The type of a variable declared using auto or decltype(auto) is deduced from its
initializer. This use is allowed when declaring variables in a block (6.3), in namespace scope (3.3.6), and in a for-init-statement (6.5.3).[...]
am i wrong in assuming that the param1 and param2 fall under block scope and hence eligible for auto deduction?
2). if such feature was allowed what would be the pitfalls?
i'm using gcc 4.8.1.
Thank you
n3690 7.1.6.4/2
The placeholder type can appear with a function declarator in the decl-specifier-seq, type-specifier-seq,
conversion-function-id, or trailing-return-type, in any context where such a declarator is valid.
7.1.6.4/3
If the auto type-specifier appears as one of the decl-specifiers in the decl-specifier-seq of a parameter-
declaration of a lambda-expression, the lambda is a generic lambda.
7.1.6.4/4
The type of a variable declared using auto or decltype(auto) is deduced from its initializer. This use is al-
lowed when declaring variables in a block (6.3), in namespace scope (3.3.6), and in a for-init-statement (6.5.3).
auto or decltype(auto) shall appear as one of the decl-specifiers in the decl-specifier-seq and the decl-
specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initial-
izer.
7.1.6.4/5
A placeholder type can also be used in declaring a variable in the condition of a selection statement (6.4) or
an iteration statement (6.5), in the type-specifier-seq in the new-type-id or type-id of a new-expression (5.3.4), in
a for-range-declaration, and in declaring a static data member with a brace-or-equal-initializer that appears
within the member-specification of a class definition (9.4.2).
Only such usage is alowed. Any other usage is prohibited (in particular usage in parameter-declaration-clause).
7.1.6.4/6
A program that uses auto or decltype(auto) in a context not explicitly allowed in this section is ill-formed.
N3690 is the committee draft for C++14, i.e. for the next C++ standard that has yet to be released and wich might not be implemented yet in most compilers. So you should refer to your compiler's documentation if generic lambdas are implemented - I guess they are not.
However, with gcc you have good chances that C++14 features will be implemented before the new standard is officially released, though you might have to explicitly enable C++14 support with a command line flag. Looking at the docs it should be -std=gnu++1y
According to this site, generic lambdas are not implemented yet in GCC.
Update:
As for normal generic functions using auto parameters: These don't exist and won't be coming for the next time. The reason is that templated functions are only slightly more verbose to type and more powerful, since you can refer to the types and directly apply template metafunctions to them. In generic lambdas this can be done only by using decltype(a), wich is a bit more tedious and has to be used with care, because it behaves a bit different than template argument deduction.
An additional bonus with templates compared to auto params is a bit more typesafety or expressiveness:
void func(auto a, auto b); //a and b might be different types
template <class T>
void func(T a, T b); //a and b must be the same type
On top of ForEveR's answer:
Why cant i do a similar thing for a normal function for e.g
void swap(auto& param1, decltype(param1)& param2)
Simply because the language doesn't allow this. Before auto was (re)invented in C++11 what you want was achievable through templates:
template <class T, class U>
void swap(T& param1, U& param2);
C++11 also brough lambda expressions and C++14 is likely to introduce polymorphic lambdas which are, basically, lambdas whose operator () are templates. A syntax similar to that of templates was considered for the polymorphic lambdas, for instance (example taken from N3418)
[]<class T>(T* p) { /* ... */ }
At the end, the prefered syntax was using auto rather than introducing a template parameter list.
It's true that one could consider extend this terser syntax to function templates (as the OP suggests) but, as far as I know, the committee hasn't considered this possibility. It might do in the future but someone has to formally propose it. This might be a "nice feature to have" but IMHO this is just syntactic sugar that doesn't bring much to the language.
Also, I can't see how this terser syntax (with no template parameter lists) could be used for template classes and perhaps diverging the syntax for template functions from that of template classes is not worth doing.
There is already a way to write what you wanted :-
template <class T>
void swap(T& param1, T& param2)
{
T temp = param1;
param1 = param2;
param2 = temp;
}
So why create a new syntax that doesn't let you do anything you couldn't do before. The suggested change to lambdas are to allow generic lambdas which you couldn't do before, and I guess any syntax using the template syntax would have been ugly here.