Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is gcc failing when using lambda for non-type template parameter?

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?

like image 993
dcmm88 Avatar asked Apr 04 '17 17:04

dcmm88


People also ask

Which parameter is allowed for non type template?

Which parameter is legal for non-type template? Explanation: The following are legal for non-type template parameters:integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.

Can you template a lambda?

Lambda-expressions are not allowed in unevaluated expressions, template arguments, alias declarations, typedef declarations, and anywhere in a function (or function template) declaration except the function body and the function's default arguments.


1 Answers

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.

like image 128
Nir Friedman Avatar answered Oct 11 '22 02:10

Nir Friedman