【问题标题】:Template specialization and explicit specification of return type vs. auto返回类型与自动的模板特化和显式规范
【发布时间】:2019-02-26 22:46:14
【问题描述】:

考虑代码:

class Test {
public:
    template<int N> auto foo() {}
    template<> auto foo<0>() { return 7;  }

    template<int N> void bar() {}
    template<> int bar<0>() { return 7;  }
};

我已经用不同的编译器测试了代码(通过Compiler Explorer)。

如果 Clang 7.0.0 foo 编译,而 bar 给出错误:

:8:20: 错误:没有函数模板与函数模板匹配 专业化“酒吧”

template<> int bar<0>() { return 7;  }

               ^

:7:26: 注意:候选模板被忽略:无法匹配 'void ()' 反对 'int ()'

template<int N> void bar() {};

                     ^

Visual C++ 同意(MSVC 19 2017 RTW):

(8): 错误 C2912: 显式特化 'int Test::bar(void)' 不是函数模板的特化

gcc 8.2 不编译任何代码(尽管原因可能是bug in C++17 support:

:5:14: 错误:非命名空间范围内的显式特化 '类测试'

 template<> auto foo<0>() { return 7;  };

          ^

:5:28: 错误:主声明中的模板 ID 'foo' 模板

 template<> auto foo<0>() { return 7;  };

                        ^

:7:26: 错误:模板参数列表过多

 template<int N> void bar() {};

                      ^~~

:8:14: 错误:非命名空间范围内的显式特化 '类测试'

 template<> int bar<0>() { return 7;  }

          ^

:8:20: 错误:预期 ';'在成员声明的末尾

 template<> int bar<0>() { return 7;  }

                ^~~

                   ;

:8:23: error: ' 之前的预期 unqualified-id

 template<> int bar<0>() { return 7;  }

                   ^

这里的正确解释是什么?对于不同的方法特化,我可以有不同的返回类型吗(为什么只使用auto,而不是在明确指定它们时)?由于我对auto 和模板的了解有限,我会说“不”。我不明白为什么要使用 auto 而不是显式命名返回类型允许针对不同的专业有不同的返回类型。

但是,这些代码是我发现 elsewhere 的代码的简化版本,所以我的解释可能不正确 - 在这种情况下,我会很感激解释 为什么在 @ 时允许不同的返回类型987654344@ 用于特化,而明确命名类型似乎是被禁止的

【问题讨论】:

  • @DanM。链接的问题与使用 auto 和在专业化中明确命名返回类型之间的差异无关。
  • 但确实如此。它引用了关于这个确切问题 AFAIU 的标准:eel.is/c++draft/dcl.spec.auto#11(这会导致编译器行为的差异)。您的示例还包含此错误:stackoverflow.com/questions/2097811/…
  • @DanM。据我了解,引用的草案说,当基本模板使用auto 时,应该使用auto 进行专门化,而在专门使用具体类型的模板时,应该命名具体类型。据我所知,在这两种情况下更改专业化返回类型的问题上是沉默的。至于您评论的第二部分(链接stackoverflow.com/questions/2097811/c):我的代码与该问题中的代码不同,因为在我的代码类中Test 不是模板,该问题有什么问题。
  • 虽然 SO question 中的示例有一个模板类,但它同样适用于常规类。见stackoverflow.com/questions/5777236。虽然它应该适用于 c++17 及更高版本,但 GCC 仍然不支持它:gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 该标准解释了为什么允许使用 foo。至于bar 的错误——简单地将函数命名为与模板相同的方式并不意味着它是一种专业化。您只能专门化模板参数。 int 不是 bar 的模板参数(但你可以这样做,在这种情况下它应该可以工作)。

标签: c++ templates


【解决方案1】:

示例代码存在几个不同的问题。

1) GCC 无法实现 CWG 727(C++17 中需要):https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282 导致 error: explicit specialization in non-namespace scope 'class Test'

2) 如果我们忽略这一点,示例代码可以简化为

template<int N> auto foo() {}
template<> auto foo<0>() { return 7; }

template<int N> void bar() {}
template<> int bar<0>() { return 7; }

仍然显示相同的错误。现在所有编译器都同意输出。他们编译 foos 并在 bar 专业化时出错。

为什么使用 auto 进行特化时允许不同的返回类型

如果特化还具有 auto 占位符,则标准允许使用 auto 返回类型特化函数

具有使用占位符类型的已声明返回类型的函数或函数模板的重新声明或特化也应使用该占位符,而不是推导类型

http://eel.is/c++draft/dcl.spec.auto#11

因此,由于这种专业化符合标准(类似于给定示例之一),并且在任何允许的地方都没有被明确禁止。

至于bar 的错误,标准说返回类型是函数模板签名的一部分:

⟨函数模板⟩名称、参数类型列表([dcl.fct])、封闭命名空间(如果有)、返回类型、模板头和尾随的要求子句([dcl.fct]) decl])(如果有的话)

http://eel.is/c++draft/defns.signature.templ

因此,在编译器看来,template&lt;&gt; int bar&lt;0&gt;() { return 7; }template&lt;... N&gt; int bar(); 模板的特化(注意返回类型)。但它之前没有声明(专业化不能在声明之前)所以编译失败!如果你添加template&lt;int N&gt; int bar();,那么它将编译(但如果你尝试调用bar,则会抱怨调用歧义)。

基本上,您不能在特化中更改函数签名,您只能特化 (duh) 模板参数(这也应该与声明中完全相同,因为它也是签名的一部分)。

我可以有一个模板特化,其显式声明的返回类型与基本模板完全不同

如上所述 - 您无法更改函数模板签名,这意味着您无法更改返回类型。但是,如果返回类型取决于模板参数,您可以专门化它!

考虑

template<int N, typename R = void> R bar() {}
template<> int bar<0>() { return 7; }
// bar<0, int> is deduced, see: http://eel.is/c++draft/temp.expl.spec#10

这是允许的,但它的缺点是当你想调用一个专业化时必须写bar&lt;0, int&gt;https://godbolt.org/z/4lrL62

这可以通过在原始声明中设置类型来解决:https://godbolt.org/z/i2aQ5Z

但是一旦专业化的数量增加,维护起来很快就会变得很麻烦。

另一个可能更易于维护的选项是返回类似ret_type&lt;N&gt;::type 的内容,并将其与bar 一起专门化。但它仍然不会像使用 auto 那样干净。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 2015-02-22
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2011-04-28
    相关资源
    最近更新 更多