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

`apply `模板在g中编译,但不在clang和vc中编译

徐栋
2023-03-14

下面的代码在g7.2.0中编译成功(编译标志是-std=c 14-Wall-Wterex-Werror-peanty-error),但在clang 5.0.0中编译失败(使用相同的标志,-std=c 14-Wall-Wterex-Werror-peanty-error)和vc 15.4(编译标志是/EHsc /Za /std: c 14 /permissive-):

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = Functor<FixedArguments..., FreeArguments...>;
};

template <typename, typename>
struct Bar{};

template <template <typename...> class>
struct Foo{};

int main()
{
    (void)Foo<apply<Bar, int, char>::type>{};
}

哪种编译器行为符合标准?如何将该模板应用更改为在clang上编译?

叮当声错误消息:

5 : <source>:5:15: error: too many template arguments for class template 'Bar'
        using type = Functor<FixedArguments..., FreeArguments...>;
                     ^                          ~~~~~~~~~~~~~~~~~
16 : <source>:16:15: note: in instantiation of template class 'apply<Bar, int, char>' requested here
    (void)Foo<apply<Bar, int, char>::type>{};
              ^
9 : <source>:9:8: note: template is declared here
struct Bar{};

VC错误消息:

5 : <source>(5): error C2977: 'Bar': too many template arguments
9 : <source>(9): note: see declaration of 'Bar'
16 : <source>(16): note: see reference to class template instantiation 'apply<Bar,int,char>' being compiled

共有2个答案

宋斌
2023-03-14

正如@T.C.在对问题的评论中指出的,此类代码格式错误(无需诊断)。

C 14标准,“名称解析”[临时决议]一节,第8段:

如果可变模板的每个有效专门化都需要一个空的模板参数包,则该模板的格式不正确,无需诊断。

C标准最新草案,第8.3段“名称解析”【临时决议】:

。。。如果出现以下情况,程序格式错误,无需诊断:

  • ...
  • 可变参数模板的每个有效特化都需要一个空模板参数包...

其他信息:核心问题2067。

根据标准要求,可以编写这样简单的解决方法:

template <template <typename...> class Functor, typename... Arguments>
struct invoke
{
    using type = Functor<Arguments...>;
};

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = typename invoke<Functor, FixedArguments..., FreeArguments...>::type;
};

现场演示

更新:正如@odinthenrd在注释中所指出的,此解决方案使用了一种额外的类型,这会导致程序编译速度变慢。

琴刚豪
2023-03-14

注意:看了这个之后,如果Bar是别名模板而不是类模板,那么这个答案是正确的。该变通方法可行,但有其他原因。有关OP的正确答案,请参阅施工人员答案。

这个问题被称为“别名缺陷”,我们在实现kvasir::mpl时遇到了很多挑战。问题是,Bar只接受两个参数,但大小。。。(固定参数)大小。。。(FreeArguments)加起来可能是2以外的东西。

编译器可以尝试通过所有别名调用跟踪潜在的arity,并且只在用户实际传递2以外的内容时才发出错误,也可以通过证明可能发生错误来“急切地”给出错误。

我发现解决这个问题的有效方法是使别名调用取决于输入的大小https://godbolt.org/g/PT4uaE

template<bool>
struct depends{
    template<template<typename...> class F, typename...Ts>
    using f = F<Ts...>;
};

template<>
struct depends<false>{
    template<template<typename...> class F, typename...Ts>
    using f = void;
};

template <template <typename...> class Functor, typename... FixedArguments>
struct apply
{
    template <typename... FreeArguments>
    using type = typename depends<(sizeof...(FixedArguments)+sizeof...(FreeArguments) == 2)>::template f<Functor, FixedArguments..., FreeArguments...>;
};

template <typename, typename>
struct Bar{};

template <template <typename...> class>
struct Foo{};

int main()
{
    (void)Foo<apply<Bar, int, char>::type>{};
}

应该注意的是,在我测试过的所有编译器上都不需要约束为正好两个,一个可以很容易地约束为sizeof。。。(固定参数)大小。。。(自由参数)!=100000,如果在具体调用中实际不起作用,编译器仍然只会发出一个错误。

实际上,我想改进我的心智模型,了解它是如何在内部工作的,以便找到更快的解决方法,在kvasir::mpl中,我们目前正在尝试在幕后手动跟踪arity,以消除依赖调用,因为依赖调用会使事情变慢一点。

 类似资料:
  • 补充clang++编译后的汇编代码: 编译结果:

  • 从原子函数指针调用函数时,如: gcc一点也不抱怨,而clang和msvc在调用时有问题: [clang]:错误:调用类型为'std::atomic的对象 Clang还指定可能的呼叫候选者为: 波动性的这种差异似乎让clang和msvc感到困惑,但海湾合作委员会却没有。 当调用从更改为

  • 问题内容: 我不得不发现我的项目中有Java代码,该代码可以在Eclipse中编译并正常运行,但是会在javac中引发编译错误。 一个完整的代码段: javac中的编译返回: 现在,此错误阻止在Maven中构建项目。由于Eclipse编译器具有更高的容忍度,因此我现在不得不假设代码段的定义和用法如上所述,静态方法不是有效的Java吗? 问题答案: 似乎Sun的1.6 JDK无法推断正确的类型。以下

  • 问题内容: 注意:这是从Comparable和Comparator合约衍生出来的,涉及null 该代码可以在Eclipse(20090920-1017)中编译并正常运行 但是它不能在上编译javac 1.6.0_17。这是错误消息: 有人可以解释为什么差异吗?这是一个错误吗?如果是这样,谁有错误? 问题答案: 这是一个已确认的错误:错误ID 6468354。这是相关的摘录: 此问题是由以下事实引起

  • 问题内容: 抱歉,标题含糊。我有这段代码可以在Eclipse Juno(4.2)上编译,但不能在javac(1.7.0_09)上编译: 错误是: 所以问题是: 这是Bug还是Eclipse错误? 有什么办法可以在不更改方法签名的情况下进行编译(即保留通配符)? 我知道将其更改为确实可以使其编译,但是我想知道是否有办法首先避免这种情况。另外,由于内容的类型需要严格的约束,因此无法将方法更改为。 问题

  • 问题内容: 我知道过去有一些关于在Eclipse中编译但不能在命令行中编译的问题,但是我还无法找到解决问题的答案。 特别是,我认为我可以设置eclipse以使用我的系统编译器,但这仍然不能解决问题。 我目前正在检查以下内容:“首选项-> Java->已安装的JRE”。 它仅包含一个JRE,这是我的系统之一。 这是问题的细节 我有一个Java通用类,将Enum类型作为参数,如下所示: 我在类中的某个