static constexpr pointer-to-function, difference between compilers - c++

When answering this question, I tried the following code with gcc (code compiled) and clang (code rejected):
typedef long (*func)(int);
long function(int) { return 42; }
struct Test
{
static constexpr func f = &function;
};
template<func c>
struct Call
{
static void f()
{
c(0);
}
};
int main()
{
Call<Test::f>::f();
}
I am not sure which compiler is right, although I think the constexpr initialization of Test::f is ok. The error clang outputs is:
error: non-type template argument for template parameter of pointer type 'func'
(aka 'long (*)(int)') must have its address taken
Which compiler is right ?
If clang is right, why , and what does this error really means ?
EDIT: for the "why", see DyP's question.

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:
[...]
— a constant expression (5.19) that designates the address of an object with static storage > duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; [...]
(n3485, emphasis mine)
I don't know exactly why it's been limited, but I think it might be related to the fact that the function address is not available at compile time (there might be a substitute for template instantiation purposes).
Edit: Enhanced answer due to a follow-up question (comment) of Synxis
constexpr func = &function;
^ this is well-formed; you can use the address of a function to initialize a constexpr object.
The problem is that it's explicitly forbidden to use pointers as non-type template arguments other than of the form &identifier:
using My_Call = Call < &function >; // fine
constexpr func mypointer = &function; // fine
using My_Ind_Call = Call < func >; // forbidden, argument not of form `&id`

Related

GCC vs Clang & MSVC Bug while using non type template parameter

I was writing a rather simple example to clarify(better understand) the concept of non type template parameters. The example is shown below. To my surprise, the given example compiles with MSVC and Clang but fails to compile with GCC(for both C++14 & C++17).
template<typename T, T a>
void f()
{
decltype(a) p; //is this valid given the call expression f<const int, 0>() below
}
int main()
{
f<const int,0>();
}
Demo
Which compiler is right here according to the standard.
To my current understanding of templates, this is what should happen:
Point 1
From temp.param#5:
The top-level cv-qualifiers on the template-parameter are ignored when determining its type.
On the first glance it seems that when applied to the given snippet, a should be int instead of const int.
But note the emphasis on the highlighted part in the above quoted statement. In particular, my interpretation of this(the phrase "when determining") is that the top-level cv-qualifiers are dropped when deducing the template parameter and so not when the template arguments are explicitly specified.
And since in my given snippet, we are explicitly specifying the template argument, there is no template argument deduction(TAD) here. Thus, the dropping of the top-level cv-qualifiers due to the above quoted temp.param#5 does not happen. Meaning the type of a is const int instead of int. This would suggest that GCC is correct in rejecting the code since we should provide an initializer for the const variable named p.
Note that i may be wrong in interpreting [temp.param#5] above.
Point 2
But from temp.param#6:
A non-type non-reference template-parameter is a prvalue. It shall not be assigned to or in any other way have its value changed.
Moreover, from expr#6:
If a prvalue initially has the type “cv T”, where T is a cv-unqualified non-class, non-array type, the type of the expression is adjusted to T prior to any further analysis.
When applying temp.param#6 and expr#6 to the given snippet, it means that a(a prvalue) will finally be of type int instead of const int. That is, this suggest that MSVC and Clang are correct in accepting the code, since p is a non-const int.
So, given the above analysis, my thinking is that the code is valid and so msvc and clang are correct in accepting it.
I believe the temp.param#5 still applies here and nothing else is needed. Let's rewrite your example to make it easier to reason about:
template<typename T>
struct Foo
{
template<T a>
static void f()
{
decltype(a) p;
}
};
int main()
{
Foo<const int>::f<0>();
}
So we have a template Foo and a function template f() as 2 separate entities with separated template headers. Now if we instantiate Foo<const int> we get the following non-template:
struct Foo
{
template<const int a>
static void f()
{
decltype(a) p;
}
};
And f() is still a function template and according to the cited standard reference decltype(a) should produce int.
You might argue that your example is different and while it is different in tokens it is not different in meaning. The non-type parameter of the original f is not deducible (doesn't have auto anywhere) it is pretty clearly specified to be of type T. It just so happens that T is something not available at the moment so until T is somehow specified, non-type parameter a makes no sense, it doesn't exist. Once you specified T it became a non-type parameter and we can apply whatever we should apply to it being an NTP.

Can lambdas be used as non-type template parameter?

Is the following code legal?
template <auto Lambda>
struct A {};
int main () {
auto lmb = [](int i){return i*i;};
A<lmb> a;
return 0;
}
I noticed that g++ compiles it fine, while clang++ returns
error: a non-type template parameter cannot have type '(lambda at main.cpp:...)'.
Can lambdas be used as non-type template parameter?
Yes, with implementations that has implemented P0732R2 - Class types in non-type template parameters but clang++ has not implemented it yet.
Source:
https://en.cppreference.com/w/cpp/compiler_support
Note that the lambda needs to be at least constexpr (which it is by default):
When this specifier is not present, the function call operator will be
constexpr anyway, if it happens to satisfy all constexpr function requirements.
You can however add constexpr to get an error on the lambda itself instead of when using it as a template parameter. As a side note: You can also specify it to be consteval to make it work as a non-type template parameter.
A stateful lambda can be constexpr:
constexpr auto lmb1 = [](int i) {
static int x = 0;
return i*i + ++x;
};
while a lambda capturing by reference, or capturing by copy and mutating (mutable), can not. Capturing by copying a constexpr is ok though.
Generic lambdas may be constexpr too:
constexpr auto gen_lmb = []<typename T>(T& val) {
val += val;
return val;
};
template <auto Lambda>
struct A {
template<typename T>
void doit(T&& arg) {
std::cout << Lambda(arg) << '\n';
}
};
//...
A<gen_lmb> ginst;
int v = 1000;
ginst.doit(v);
ginst.doit(std::string("foo "));
std::cout << v << '\n';
2000
foo foo
2000
[temp.arg.nontype]/1:
If the type T of a template-parameter contains a placeholder type ([dcl.spec.auto]) or a placeholder for a deduced class type
([dcl.type.class.deduct]), 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 ([temp.param]), the program is
ill-formed.
So, the rules are set by [temp.param]/6:
A non-type template-parameter shall have one of the following
(possibly cv-qualified) types: ...
(6.1) a structural type ...
The rules for structural type are: --my emphasis--
(7.1) a scalar type, or
(7.2) an lvalue reference type, or
(7.3) a literal class type with the following properties:
(7.3.1) all base classes and non-static data members are public and non-mutable and
(7.3.2) the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.
Since the lambda has no base class, the only requirement is that it has to be a literal class type ([basic.types]) which includes:
(10.5.2) ... a closure type ([expr.prim.lambda.closure]) ...
The data members of a structural type shall also be structural type, this applies to the lambda's capture in this case, as long as all its members are public and non-mutable.
#Nicol Bolas commented below that a lambda with captures, even if constexpr literal type captures, is not mandated by the standard to manage the captures as public fields.
The bottom line is that in C++20 a constexpr lambda expression without a capture shall be legal as a template non-type argument (based on [basic.types]/10.5.2 mentioned above).
See also an answer by #Barry to a similar question.
Below code compiles with gcc, but as I understand from the comment by Nicol Bolas, not all cases are guaranteed by the spec (or even worse, all cases are not guaranteed by the spec?).
Suppose we have:
template <auto T> struct A {};
struct B {};
struct C {
~C(){}
};
Literal type lambdas, that shall be legal as template arguments:
// compiles in gcc and should be ok by the spec as of [basic.types]/10.5.2
A<[](){}> a; // compiler deduces the anonymous lambda to be constexpr
auto lmb1 = [](){};
// same as above
A<lmb1> a1;
// compiler deduces lmb1 above to be constexpr
// same as it will deduce the following:
B b {};
A<b> here_i_am;
Lambdas, that are compiled by gcc as template arguments, but as Nicol Bolas argues in the comment - the spec doesn't guarantee them to be literal types:
const int i = 0;
constexpr auto lmb2 = [i](){};
// compiles in gcc but is not guaranteed by the spec
A<lmb2> a2;
constexpr auto lmb3 = [b](){}; // B is literal
// compiles in gcc but is not guaranteed by the spec
A<lmb3> a3;
Non-literal type lambdas, not legal as template arguments:
const int j = 0;
// below doesn't compile: <lambda()>{j} is not a constant expression
constexpr auto lmb4 = [&j](){}; // local reference - not constexpr
A<lmb4> a4;
C c;
// below doesn't compile: <lambda()>'{c} does not have 'constexpr' destructor
constexpr auto lmb5 = [c](){}; // C is not literal
A<lmb5> a5;
First, I think your lambda should be constexpr to be used as a non-type template parameter. I find it a bit weird, that it works.
But then it should work in this case. The Standard tells us that a non-type template parameter can be a literal class type (that's a bit iffy since closures are literal but not really class types, I think they are explicitly included here) with the additional requirements that
All base clases and non-static members must be non-mutable and public
Their types must be structural or arrays thereof
So we don't have problems in this easy example. But if you capture anything, your lambda has a non-public member variable and should be out. If that is not so sharp for closures, it definitely stops working if you capture something non-constexpr.

Dependent type or argument in decltype in function definition fails to compile when declared without decltype

I've been playing with deduced return types in definitions that resolve to the same type as the declaration. This works:
template <typename>
struct Cls {
static std::size_t f();
};
template <typename T>
decltype(sizeof(int)) Cls<T>::f() { return 0; }
But if I change the definition to something that should be equivalent by replacing sizeof(int) with sizeof(T) it fails
template <typename T>
decltype(sizeof(T)) Cls<T>::f() { return 0; }
gcc's error (clang is almost identical):
error: prototype for ‘decltype (sizeof (T)) Cls<T>::f()’ does not match any in class ‘Cls<T>’
decltype(sizeof(T)) Cls<T>::f() { return 0; }
^~~~~~
so.cpp:4:24: error: candidate is: static std::size_t Cls<T>::f()
static std::size_t f();
^
The same problem arises with function parameter types:
template <typename>
struct Cls {
static void f(std::size_t);
};
template <typename T>
void Cls<T>::f(decltype(sizeof(T))) { } // sizeof(int) works instead
Stranger yet, if the declaration and definition match and both use decltype(sizeof(T)) it compiles successfully, and I can static_assert that the return type is size_t. The following compiles successfully:
#include <type_traits>
template <typename T>
struct Cls {
static decltype(sizeof(T)) f();
};
template <typename T>
decltype(sizeof(T)) Cls<T>::f() { return 0; }
static_assert(std::is_same<std::size_t, decltype(Cls<int>::f())>{}, "");
Update with another example. This isn't a dependent type but still fails.
template <int I>
struct Cls {
static int f();
};
template <int I>
decltype(I) Cls<I>::f() { return I; }
If I use decltype(I) in both the definition and declaration it works, if I use int in both the definition and declaration it works, but having the two differ fails.
Update 2:
A similar example. If Cls is changed to not be a class template, it compiles successfully.
template <typename>
struct Cls {
static int f();
using Integer = decltype(Cls::f());
};
template <typename T>
typename Cls<T>::Integer Cls<T>::f() { return I; }
Update 3:
Another failing example from M.M. A templated member function of a non-templated class.
struct S {
template <int N>
int f();
};
template <int N>
decltype(N) S::f() {}
Why is it illegal for the declaration and definition to disagree with a dependent type only? Why is it affected even when the type itself isn't dependent as with the template <int I> above?
Because when there is a template parameter involved, decltype returns an unqiue dependent type according to the standard, see below. If there is no template parameter then it resolves to an obvious size_t. So in this case you have to choose either both declaration and definition have an independent expression (e.g. size_t/decltype(sizeof(int))), as a return type, or both have dependent expression (e.g. decltype(sizeof(T))), which resolved to an unique dependent type and considered to be equivalent, if their expressions are equivalent (see below).
In this post I am using the C++ standard draft N3337.
§ 7.1.6.2 [dcl.type.simpl]
¶ 4
The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e)
is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded func-
tions, the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
— otherwise, decltype(e) is the type of e.
This explains what is decltype(sizeof(int)). But for decltype(sizeof(T)) there is another section explaining what it is.
§ 14.4 [temp.type]
¶ 2
If an expression e involves a template parameter, decltype(e) denotes a unique dependent type. Two such
decltype-specifiers refer to the same type only if their expressions are equivalent (14.5.6.1). [ Note: however,
it may be aliased, e.g., by a typedef-name. — end note ]
In Clang LLVM sources version 3.9 in file lib/AST/Type.cpp
DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
// C++11 [temp.type]p2: "If an expression e involves a template parameter,
// decltype(e) denotes a unique dependent type." Hence a decltype type is
// type-dependent even if its expression is only instantiation-dependent.
: Type(Decltype, can, E->isInstantiationDependent(),
E->isInstantiationDependent(),
E->getType()->isVariablyModifiedType(),
E->containsUnexpandedParameterPack()),
The important phrase starts as "Hence a decltype...". It again clarifies the situation.
Again in Clang sources version 3.9 in file lib/AST/ASTContext.cpp
QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
DecltypeType *dt;
// C++11 [temp.type]p2:
// If an expression e involves a template parameter, decltype(e) denotes a
// unique dependent type. Two such decltype-specifiers refer to the same
// type only if their expressions are equivalent (14.5.6.1).
if (e->isInstantiationDependent()) {
llvm::FoldingSetNodeID ID;
DependentDecltypeType::Profile(ID, *this, e);
void *InsertPos = nullptr;
DependentDecltypeType *Canon
= DependentDecltypeTypes.FindNodeOrInsertPos(ID, InsertPos);
if (!Canon) {
// Build a new, canonical typeof(expr) type.
Canon = new (*this, TypeAlignment) DependentDecltypeType(*this, e);
DependentDecltypeTypes.InsertNode(Canon, InsertPos);
}
dt = new (*this, TypeAlignment)
DecltypeType(e, UnderlyingType, QualType((DecltypeType *)Canon, 0));
} else {
dt = new (*this, TypeAlignment)
DecltypeType(e, UnderlyingType, getCanonicalType(UnderlyingType));
}
Types.push_back(dt);
return QualType(dt, 0);
}
So you see Clang gathers and picks those unique dependent types of decltype in/from a special set.
Why compiler is so stupid that it does not see that the expression of decltype is sizeof(T) that is always size_t? Yes, this is obvious to a human reader. But when you design and implement a formal grammar and semantic rules, especially for such complicated languages as C++, you have to group problems up and define the rules for them, rather than just come up with a rule for each particular problem, in the latter way you just wont be able to move with your language/compiler design. The same here there is no just rule: if decltype has a function call expression that does not need any template parameters resolution - resolve decltype to the return type of the function. There is more than that, there are so many cases you need to cover, that you come up with a more generic rule, like the quoted above from the standard (14.4[2]).
In addition, a similar non-obvious case with auto, decltype(auto) found by AndyG in C++-14 (N4296, § 7.1.6.4 [dcl.spec.auto] 12/13):
§ 7.1.6.4 [dcl.spec.auto]
¶ 13
Redeclarations or specializations of a function or function template with a declared return type that uses a
placeholder type shall also use that placeholder, not a deduced type. [ Example:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
decltype(auto) f(); // error, auto and decltype(auto) don’t match
Changes in C++17, Document Number >= N4582
Change in the standard draft N4582 from March 2016 (thanks to bogdan) generalizes the statement:
§ 17.4 (old § 14.4) [temp.type]
¶ 2
If an expression e is type-dependent (17.6.2.2), decltype(e) denotes a unique dependent type. Two such
decltype-specifiers refer to the same type only if their expressions are equivalent (17.5.6.1). [ Note: however,
such a type may be aliased, e.g., by a typedef-name. — end note ]
This change leads to another section describing the type dependent expression that looks quite strange to our particular case.
§ 17.6.2.2 [temp.dep.expr] (old § 14.6.2.2)
¶ 4
Expressions of the following forms are never type-dependent (because the type of the expression cannot be
dependent):
...
sizeof ( type-id )
...
There are further sections on value-dependent expressions where sizeof can be value-dependent if the type-id dependent. There is no relation between value-dependent expression and decltype. After some thinking, I did not find any reason why decltype(sizeof(T)) must not or cannot resolve into size_t. And I would assume that was quite sneaky change ("involves a template parameter" to "type-dependent") in the standard that compiler developers did not pay much attention to (maybe overwhelmed by many other changes, maybe did not think that it might actually change something, just a simple formulation improvement). The change does make sense, because sizeof(T) is not type-dependent, it is value-dependent. decltype(e)'s operand is a unevaluated operand, i.e. does not care about value, only about type. That is why decltype returns a unique type only when e is type-dependent. sizeof(e) might be only value-dependent.
I tried the code with clang 5, gcc 8 -std=c++1z - the same result: error. I went further and tried this code:
template <typename>
struct Cls {
static std::size_t f();
};
template <typename T>
decltype(sizeof(sizeof(T))) Cls<T>::f() {
return 0;
}
The same error was given, even that sizeof(sizeof(T)) is neither type- or value-dependent (see this post). This gives me a reason to assume that the compilers are working in an old way of C++-11/14 standard (i.e. "involves a template parameter") like in the source snippet above from clang 3.9 source (I can verify that the latest developing clang 5.0 has the same lines, have not found anything related to this new change in the standard), but not type-dependent.

Does static object in a template function have linkage?

This question refers to my previous question: clang does not compile my code, but g++ does. From my research, the issue at stake boils down to linkage, does the static variable data have linkage in the sample below (it compiles with g++-4.8.1)? How come it has linkage (I would not otherwise be able to instantiate with a non-type template parameter)?
template <int const* ptr>
void foo()
{
}
typedef void (*func_type)();
template <int = 0>
void run_me()
{
static int data;
func_type const f1 = foo<&data>;
// auto f2 = foo<&data>; // doesn't work with gcc
// foo<&data>(); // doesn't work with gcc
}
int main(int, char*[])
{
run_me();
return 0;
}
Obligatory quote from the standard:
A template-argument for a non-type, non-template template-parameter shall be one of:
...
— a constant expression (5.19) that designates the address of an object with static storage duration and
external or internal linkage or a function with external or internal linkage, including function templates
and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as
& id-expression, except that the & may be omitted if the name refers to a function or array and shall
be omitted if the corresponding template-parameter is a reference; or
...
Surely a static variable declared in a function (whether or not it is a template function) has no linkage.
§3.5 para. 8: "except as noted, a name declared at block scope (3.3.3) has no linkage"
The only exceptions listed in that clause, as far as I can see, are provided in para. 6: "The name of a function declared in block scope and the name of a variable declared by a block scope extern declaration have linkage."
However, it's possible that 14.3.2 might be loosened at some point.
Daniel Krügler submitted DR 1451 on 2012-02-01:
According to 14.3.2 [temp.arg.nontype] paragraph 1 bullet 3, only objects with linkage can be used to form non-type template arguments. Is this restriction still needed? It would be convenient to use block-scope objects as template arguments.
The DR was closed on the basis that it is an extension request and should be dealt with by the Evolution Working Group. It seems to have been included in n3413, "Allowing arbitrary literal types for non-type template parameters".
So it's certainly conceivable that one or more C++ compilers might choose to implement a looser restriction on non-type template parameters.

Instantiating a template class with NULL argument

I understand from bunch of other Stackoverflow threads (like this) that Template arguments are evaluated at Compile Time.
Also, non-type template-parameter should be either a constant expression, integral expression or pointer to an object with external linkage.
And, I am not using --std=c++0x in my g++ command in Makefile.
So, is it possible to instantiate a template class with NULL passed as argument?
// I have a template class like this -
template<class T, T invalidVal, int e> class A
{
static inline bool dummy(T value)
{
return 0;
}
}
#define MY_INVALID_VAL ((void *)0)
// Now I want create class from the above template class with
// T=void*, invalidVal=NULL & e=0
typedef A<void *, MY_INVALID_VAL, 1> ClassA;
The above code compiles fine on MS Visual Studio 2008.
On g++ - I get the error - "a cast to a type other than an integral or enumeration type cannot appear in a constant-expression"
I tried out a few options after googling -
Declare "extern void *MY_INVALID_VAL;" in a header file - include it and do
void MY_INVALID_VAL=NULL; before template instantiation.
In that case, I get error "MY_INVALID_VAL is not a valid template argument for type 'void' because it is not a constant pointer"
So my question is -
Is there no way of instantiating a template class with NULL argument without using c++0x standard?
Thanks!
EDIT:
Thanks for all the comments and thanks for citing exact section from standards' draft.
Just listing down the things I tried -
1) Passing "0" directly doesn't work.
Error I get is - "could not convert '0' to template argument void *"
2) Declaring static const void *my_null=0; and passing my_null doesn't work.
It gave error - "my_null can not appear in constant expression"
3) Tried the pointer to null Object (null object pattern) approach suggested in one of the comments
See below -
class my_null
{
public:
my_null() { my_null_ptr = NULL; }
void * get() { return my_null_ptr; }
private:
void *my_null_ptr;
};
my_null my_null_obj;
my_null *ptr = &my_null_obj;
typedef A<void *, (void *)ptr, 1> ClassA;
Still I get error - "ptr can not appear in constant expression"
So now this has me wondering - what value should I pass to make it work?
Or is there no possible to make it work? (I mean a way that does not involve using c++11 std)
I haven't -yet- found a value that will succeed the compilation.
Any help appreciated (and needed :-P)!
As a sidenote, one more thing that I would want to ask is - is there any pointer value that I can use for non-type template argument?
C++98 says that non-type template arguments shall be one of
an integral constant-expression of integral or enumeration type; or
the name of a non-type template-parameter; or
the name of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as id-expression; or
the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as &id-expression where the & is option if the name refers to a function or array; or
a pointer to member expressed as described in 5.3.1.
Null pointers do not fall under any item of this list, and therefore it is not valid to pass a null pointer as a non-type template parameter.
C++11 updates this list to
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
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1.
The updated requirements do cover null pointers. Therefore, to use null pointers as non-type template arguments you must use C++11.
Try this:
static const void* my_null_pointer = 0;
typedef A<void *, my_null_pointer, 1> ClassA;
Something like that worked for me on MSVC and gcc as well.
Passing 0 as that template parameter should work as well.
typedef A<void *, 0, 1> ClassA;