Brent
Brent

Reputation: 4283

Non-pointer function type can be a non-type parameter?

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.

Upvotes: 4

Views: 199

Answers (2)

Seth Carnegie
Seth Carnegie

Reputation: 75130

§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.

Upvotes: 4

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153840

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.

Upvotes: 2

Related Questions