模板专用化和明确指定返回类型与自动
Template specialization and explicit specification of return type vs. auto
考虑代码:
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; }
};
我已经用不同的编译器(通过编译器资源管理器)测试了代码。
如果Clang 7.0.0foo
编译,而bar
给出错误:
:8:20:错误:没有与函数模板匹配的函数模板专业化‘bar’
template<> int bar<0>() { return 7; } ^
:7:26:注意:已忽略候选模板:无法匹配"void"()'对'int()'
template<int N> void bar() {}; ^
Visual C++同意(MSVC 19 2017 RTW):
(8):错误C2912:显式专用化'int测试::bar(void)'不是函数模板的专门化
gcc 8.2没有编译任何代码(尽管原因可能是C++17支持中的一个错误:
:5:14:错误:非命名空间范围中的显式专用化"等级测试">
template<> auto foo<0>() { return 7; }; ^
:5:28:错误:模板id'foo<主声明中的"0>"模板
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:错误:"<"之前应为不合格的id代币
template<> int bar<0>() { return 7; } ^
这里的正确解释是什么?对于不同的方法专门化,我可以有一个不同的返回类型吗(为什么只使用auto
,而不是显式指定它们)?由于我对auto
和模板的了解有限,我会说"不"。我不明白为什么使用auto
而不是显式命名返回类型,可以为不同的专业化提供不同的返回类型。
然而,这些代码是我在其他地方发现的代码的简化版本,所以我的解释可能是不正确的——在这种情况下,我将感谢解释为什么在使用auto
进行专门化时允许不同的返回类型,而显式命名类型似乎是被禁止的。
示例代码存在几个不同的问题。
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; }
其仍然表现出相同的误差。现在,所有编译器都对输出达成一致。他们编译foo
,并在bar
专用化上出错。
为什么在使用auto进行专业化时允许不同的返回类型
如果专门化还具有auto
占位符,则标准允许专门化具有auto
返回类型的函数
函数或函数模板的重新声明或特殊化,如果声明的返回类型使用占位符类型,则也应使用该占位符,而不是推导类型
http://eel.is/c++draft/dcl.spec.auto#11
因此,由于这种专业化符合标准(类似于给定的例子),并且在任何允许的地方都没有被明确禁止。
至于bar
的错误,标准规定返回类型是函数模板签名的一部分:
⟨函数模板名称、参数类型列表([dcl.fct])、封闭命名空间(如果有)、返回类型、模板头和尾部requires子句([dcl.dell])(如果有的话)
http://eel.is/c++draft/defs.signature.templ
因此,在编译器看来,template<> int bar<0>() { return 7; }
是template<... N> int bar();
模板的特殊化(注意返回类型)。但它以前没有声明(特殊化不能先于声明),所以编译失败!如果您添加template<int N> 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<0, int>
:https://godbolt.org/z/4lrL62
这可以通过在原始声明中使类型成为条件类型来解决:https://godbolt.org/z/i2aQ5Z
但一旦专业化的数量增加,维护起来就会很麻烦。
另一个可能更易于维护的选项是返回类似ret_type<N>::type
的内容,并将其与bar
一起进行专门化。但它仍然不会像使用auto
那样干净。
- 如何获取std::result_of函数的返回类型
- 奇怪的结构&GCC&clang(void*返回类型)
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 特征::矩阵<双精度,1,3> 结构类型函数中的返回类型函数
- 模板专用化和明确指定返回类型与自动
- 将返回类型专用化为 void 或 const 左值引用
- 有没有办法根据 lambda 参数返回类型部分专用化我的模板化函数?
- 具有替代返回类型的模板专用化
- 如何使用类型专用化模板方法,该类型本身就是一个模板,其中只有返回类型依赖于模板类型
- 如何在返回类型函数模板的专用化中使用派生类型?( "couldn't infer template argument" )
- C++模板专用化,类作为返回类型,枚举作为参数
- C++显式返回类型模板专用化
- 返回类型的C++11 方法模板专用化
- 具有模板专用化的重载返回类型
- C++模板专用化:更改运算符()的返回类型
- 为什么模板专用化不能更改返回类型
- 代码重复和模板专用化(当专用函数具有不同的返回类型时)
- 具有不同返回类型的显式专用化模板类成员函数