当前位置: 首页 > 知识库问答 >
问题:

为什么在将 lambda 用于非类型模板参数时 gcc 会失败?

谢选
2023-03-14

以下片段使用Clang 4.0编译没有错误,但GCC 7.0会产生错误(注意使用-std=c 1z标志)。

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);
}

具体来说,GCC抱怨lambda和嵌套类的方法都没有链接,因此它们不能用作非类型模板参数。

至少对于lambda的情况,我认为Clang是正确的(而GCC是错误的),因为(引用转换操作符cppreference):

此转换函数返回的值是指向具有C语言链接的函数的指针,调用该函数时,其效果与直接调用闭包对象的函数调用操作符相同。

GCC行为不端吗?

共有2个答案

毛胜
2023-03-14

我同意尼尔的回答,并想补充一些信息。他引用了标准中的相关章节(§14.3.2[temp.arg.notype]),该章节表明,不再要求非类型参数具有链接,但这仍然不能说明GCC对于lambda部分表现不好。为此,我们需要显示static_cast

§5.1.5 Lambda表达式【expr.prim.Lambda】:

有趣的是,GCC声称已经在已经发布的版本6中实现了这一点(N4268)(如果你想通过说GCC 7还没有正式发布来原谅GCC的行为,那么也许当它发布时这将被修复):

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

所以总而言之,这是GCC中的一个错误。

陆畅
2023-03-14

根据http://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter,自C 17以来,外部联系似乎不再是一项要求。http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf[temp.arg.nontype]下的C 17草案中也有同样的语言(注意,它与C 14草案的联系不正确)。

可以与非类型模板参数一起使用的模板参数可以是模板参数类型的任何转换常量表达式…

唯一的例外是引用和指针类型的非类型模板参数不能引用/成为的地址

    < li >子对象(包括非静态类成员、基子对象或数组元素); < li >临时对象(包括在引用初始化期间创建的对象); < li >字符串文字; < Li > typeid的结果; < li >或预定义变量__func__。

关于cppreference的链接也特别提到了C 17之前的函数指针:

在实例化具有非类型模板参数的模板时,以下限制适用:

...

对于指向函数的指针,有效参数是指向具有链接的函数的指针(或计算为空指针值的常量表达式)。

由于你的问题被标为C1z(我们现在应该有一个17的标签,而不是17的标签),我们应该使用第一组规则。您的示例似乎不属于C17的任何异常类别,因此gcc是错误的。

请注意,如果将语言标志更改为14,那么clang不会编译示例。

 类似资料:
  • 我在一个项目中,必须使用一个常量模板对象的引用作为另一个对象模板的参数。 简单地说,我想这样做: 问题是我不知道如何让它发生,我需要你的帮助。 在visual studio上,上述代码将产生以下错误:“C2971:具有非静态存储持续时间的变量不能用作非类型参数” 如果我尝试使用 ,则进行以下更改: 我得到以下错误“C2131:表达式未计算为常数” 好吧,我确实尝试了一些我在其他帖子上看到的关于类似

  • 我正在学习C 17非类型模板参数的新功能。我编写了一个简单的代码片段,如下所示: 据我所知,福 但是,语句使用clang、MSVC 19.27编译,但在GCC 10.2、MSVC 19.25编译时失败。 我的问题是:为什么编译器的行为不同?标准对此有何规定? 链接到编译器资源管理器: 叮当声https://godbolt.org/z/66M695 海湾合作委员会https://godbolt.or

  • 我正在尝试编写一个简单的模板,我可以使用该模板对带有单个参数的函数进行记忆: 但我得到了一个错误: 错误:没有匹配函数来调用“备忘录(双精度)” 注:候选人是: 注意:模板OUT记忆(IN) 注意:模板参数扣除/替换失败: 为什么编译失败? 实际上,当我指定所有模板参数时,我不明白为什么模板参数推导/替换会发生。 我使用的是gcc版本4.7.2(未启用C 11) PS:模板的错误比我最初意识到的要

  • 这些代码如下: 使用G++4.8.2错误信息编译:

  • OpenGL定义了C函数来管理资源。我编写了一个简单的包装器来以RAII的方式处理它们。函数对类似于和。但是,也有一些函数对适用于资源数组,例如和。对于前者,我编写了一个简单的类来完成这项工作,对于后者,我编写了另一个处理数组的类。然而,我注意到有时我只使用一个缓冲区或纹理,在那里我不必承担向量的费用,我想如果发布函数在开始时采用大小参数,我会专门化类析构函数,但是... 对于上述SSCCE g树

  • 我有以下代码: 这段代码会导致编译错误。使用< code>g -std=c 1z编译时,错误显示如下: 使用< code>clang -std=c 1z时,错误为: 我在MSYS2 MinGW-w64环境中运行这些。我的GCC版本是GCC 7.1.0,我的Clang版本是4.0.0;我在GCC和Clang中使用的标准库是与我的GCC编译器捆绑在一起的libstdc。 在我看来,对函数templat