I'm trying to do this template instantiation but it's not working. I'm getting the errors:
prog.cpp:7:15: error: template-id 'f<const A&, A()>' for 'void f()' does not match any template declaration
template <class T, T> void f() {}
struct A {};
template void f<const A &, A()>();
int main() {}
This is weird, because when I do it in main it works:
int main() {
const A &a = A(); // no error
}
So why doesn't it work in the template line?
Possible duplicate of Non-type template parameters
These are the rules of template non type parameters
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
integral or enumeration type,
pointer to object or pointer to function,
lvalue reference to object or lvalue reference to function,
pointer to member,
std::nullptr_t.
What you are passing is an RValue (temporary object, etc which cannot be assigned to), which does not fall under any of these possibilities.
edit:
It appears that it is infact being interpreted as a function type, but your template signature expects a non type parameter of type A (exactly a const A&)
A template argument cannot be a temporary object. Only primitive types which can reasonably be compared for exact equality may be template non-type arguments. This includes
integers,
enumerators, and
pointers to objects with extern linkage.
But
floating-point numbers aren't allowed because they can be very close yet not equal
static objects might have the same name but different locations in different files, which would make the template-id confusingly resolve to different instantiations with the same name in different files
same goes for string literals
temporary objects don't have consistent addresses so you can't pass a pointer to one
the value of a temporary object as you passed, which can't even be tested for equality, would never let the language match one template instantiation to another!
(As Pubby notes, A() is actually interpreted as the type of a function with no parameters returning A. So the compiler is just failing to find a template declaration taking two type parameters.)
Related
Consider a function template f that binds a non-const lvalue reference to a deduced non-type template parameter
template <auto N>
void f()
{
auto & n = N;
}
This works when f is instantiated over class types
struct S {};
f<S{}>(); // ok
But doesn't work when instantiated over non class types (as I expected)
f<42>(); // error non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
The same error is emitted if an lvalue argument is used to instantiate f as well
constexpr int i = 42;
f<i>(); // also error
Here's a demo to play with.
This looks like non-type template parameters of class type are lvalues, which seems odd. Where does the standard make a distinction between these kinds of instantiations, and why is there a difference if the argument to the template is of class type?
The reason for the distinction is because class non-type template parameters weren't always there. Originally, value template parameters could only be pointers, integers, or a few other things. Such parameters were simple and the value was just a single number known at compile-time. As such, making them rvalues (remember: prvalue is C++11) was OK.
Once NTTPs could be more complex object types, you have to start engaging with certain questions. Should you be able to do this:
template<std::array<int, 5> arr>
void func()
{
for(int i: arr)
//stuff
}
The obvious answer is "of course you should." But that would require that you can get a reference to arr itself. That's how range-based for is defined, after all.
Now that can still work. After all, range-based for uses auto&& to store its reference, so it can reference a prvalue. But that has consequences.
Namely, if you create a reference to a prvalue, that causes the materialization of a temporary. A new temporary object distinct from all other objects.
This means that if you use a class NTTP in multiple places, you will get different objects with different addresses and different addresses for their subobjects. And this is something you can detect, since you can get the addresses of their subobjects.
Forcing compile-time code to create temporaries every time you use the name is bad for performance. So two different uses of such a parameter need to result in talking about the same object.
Therefore, class NTTPs need to be lvalues; each use of the name within a template is referring to the same object. But you can't go back and make all existing NTTPs lvalues too; that would break existing code.
So that's where we are.
As for where this is defined, it is in [temp.param]/8:
An id-expression naming a non-type template-parameter of class type T denotes a static storage duration object of type const T, known as a template parameter object, whose value is that of the corresponding template argument after it has been converted to the type of the template-parameter. All such template parameters in the program of the same type with the same value denote the same template parameter object.
Consider the following class S containing a function pointer, and a constexpr object s of that class initialized with a lambda:
struct S
{
void (*f) ();
};
constexpr S s { []{} };
Now if I write a template X with a non-type template parameter of type S, and instantiate it over s like this:
template<S> struct X {};
using x = X<s>;
clang compiles the code, but gcc complains with:
error: '<lambda()>::_FUN' is not a valid template argument of type 'void (*)()' because '<lambda()>::_FUN' is not a variable
using x = X<s>;
^
Here's the program.
The code seems fine to me, and I'm not sure what the error message is referring to. So is this code valid?
Note that both compilers accept the code if X instead has a non-type template parameter of reference type to S, like this:
template<S const &> struct X {};
This question was inspired by another similar question.
The code is valid.
[temp.arg.nontype]/2:
A template-argument for a non-type template-parameter shall be a converted constant expression ([expr.const]) of the type of the template-parameter.
[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 [...]
(There's no implicit conversion in this case.)
[expr.const]/11:
A constant expression is [...] or a prvalue core constant expression whose value satisfies the following constraints:
if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
if the value is of pointer type, it contains [...] the address of a non-immediate function [...],
if the value is of pointer-to-member-function type, [...],
if the value is an object of class or array type, each subobject satisfies these constraints for the value.
An entity is a permitted result of a constant expression if [...] or if it is a non-immediate function.
(All functions are non-immediate except for the consteval ones. [dcl.constexpr])
Therefore, s is a valid converted constant expression of type S. In addition, it is not subject to [temp.arg.nontype]/3 (which only applies to pointer/reference to objects).
That is, s is a valid template argument.
I'm not sure what the error message is referring to
It's nonsense.
The error is emitted from invalid_tparm_referent_p inside GCC. It was extracted from code for handling pointer to object type when class non-type template paramter was implemented (4be5c72). Apparently, the implementer forgot to update this function to account for the pointer-to-function case.
I've reported the bug as https://gcc.gnu.org/PR97700.
The title is quoted from this SO answer. It is discussing using SFINAE to detect the existence of a member function with the given signature and points out a failing of the method in the accepted answer when dealing with inherited member functions. In particular, the explanation given is as follows
If you are not already wise to this gotcha, then a look at of the definition of std::shared_ptr<T> in the header will shed light. In that implementation, std::shared_ptr<T> is derived from a base class from which it inherits operator*() const. So the template instantiation SFINAE<U, &U::operator*> that constitutes "finding" the operator for U = std::shared_ptr<T> will not happen, because std::shared_ptr<T> has no operator*() in its own right and template instantiation does not "do inheritance".
This snag does not affect the well-known SFINAE approach, using "The sizeof() Trick", for detecting merely whether T has some member function mf (see e.g. this answer and comments).
Using the terminology from the answer, what is the difference between using T::mf as a template argument to instantiate a type vs having the compiler determine it through a template function argument deduction? What does "template instantiation does not do inheritance" mean? And lastly, why doesn't this affect simply checking for existence of a member, like here?
Minimized example:
struct A {
void a() const;
};
struct B : A {};
template<typename U, void (U::*)() const> struct SFINAE {};
template<typename U> void Test(SFINAE<U, &U::a>*) { }
int main(void)
{
Test<B>(0); // doesn't compile
return 0;
}
Demo.
The problem is that when B::a is inherited from A, the type of &B::a is actually "pointer to member of A" - and, while normally a pointer-to-member-of-base can be implicitly converted to pointer-to-member-of-derived, this conversion doesn't apply for non-type template arguments, per §14.3.2 [temp.arg.nontype]/p5:
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 type pointer to member function,
if the template-argument is of type std::nullptr_t, the
null member pointer conversion (4.11) is applied; otherwise, no
conversions apply. If the template-argument represents a set of
overloaded member functions, the matching member function is selected
from the set (13.4).
I've been experimenting with function types in C++. Note that I don't mean pointer-to-function types like:
typedef void (*voidFuncPtr)();
but the more exotic:
typedef void (voidFunc)();
I didn't expect the following code to compile, but surprisingly it did:
template<voidFunc func>
class funcClass
{
public:
void call() { func(); };
};
void func()
{ }
void Test()
{
funcClass<func> foobar;
foobar.call();
}
however, if I try adding the following to funcClass:
voidFuncPtr get() { return &func; }
I get the error Address expression must be an lvalue or a function designator
My first question here is: what kind of black magic is the compiler using to pretend that a func type is something it can actually pass around an instance of? Is it just treating it like a reference? Second question is: if it can even be called, why can't the address of it be taken? Also, what are these non-pointer-to function types called? I only discovered them because of boost::function, and have never been able to find any documentation about them.
§14.1.4 of the Standard says:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
— integral or enumeration type,
— pointer to object or pointer to function, [this is what yours is]
— lvalue reference to object or lvalue reference to function,
— pointer to member,
— std::nullptr_t.
And §14.1.6 says
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. A non-type
non-reference template-parameter cannot have its address taken. When a
non-type non-reference template-parameter is used as an initializer
for a reference, a temporary is always used.
So that explains the two behaviours you are seeing.
Note that func is the same as &func (§14.3.2.1):
[A non-type template parameter can be] 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...
So it's just a function pointer.
Given that the code compiles without the address-of operator and pointers (including to functions and member functions) are valid template arguments, it seems the compiler considers voidFunc to be a function pointer type, i.e., the decayed version of the type. The rules for this didn't change between C++ 2003 and C++ 2011.
class Example {
// ...
};
template <typename T, Example ex> //Error
class MyExample{
// ...
};
My question is why can't template non-type parameters be of class type?
The error that I get is
error: ‘class Example’ is not a valid type for a template constant parameter
Simply, because those are the rules. Rationally, template parameters have to be resolved at compile time and objects of class type are only constructed (even temporaries and those with static storage duration) at run time. You can only have template parameters that are "values" resolvable at compile time such as integers and types. It is possible to have template parameters that are pointers or references to objects, though.
According to c++ standard,
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
— integral or enumeration type,
— pointer to object or pointer to function,
— reference to object or reference to function,
— pointer to member.
A non-type template-parameter shall not be declared to have floating point, **class**, or void type.
It is obvious that any std conforming compiler throws an error if you declare class as non type template argument.
Starting with C++ 20 this is now supported
The classes generally need to be a Literal Type.