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

2

16

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?

Misbelief answered 4/4, 2017 at 17:34 Comment(1)
Related: #18499457Milkweed
D
4

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.

Desman answered 4/4, 2017 at 23:25 Comment(4)
N4296 is a C++17 draft. N4140 is (almost) the C++14 publication.Antonyantonym
Has isocpp.org still not corrected their claim that N4296 is a C++14 draft?Cordle
I think the question for c++14 is what kind of linkage does the closure type's operator () have?Misbelief
Ok, I will edit this when I have a moment. Since the question was for 17 it doesn't change the answer substantially.Desman
M
2

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]:

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

Misbelief answered 5/4, 2017 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.