[英]How to deduce template return type with another template parameter with explicit specialization?
[英]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; }
};
我已经使用不同的编译器(通过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不会编译任何代码(尽管原因可能是C ++ 17支持中的错误 :
:5:14:错误:非命名空间范围“类测试”中的显式专业化
template<> auto foo<0>() { return 7; }; ^
:5:28:错误:主模板的声明中的template-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:错误:“ <”令牌之前的预期unqualified-id
template<> int bar<0>() { return 7; } ^
这里的正确解释是什么? 我可以针对不同的方法专长使用不同的返回类型吗(为什么只使用auto
,而没有在明确指定它们时指定)? 由于对auto
和template的了解有限,我会说“ no”。 我不明白为什么要使用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
s并在专业bar
上出错。
为什么使用auto进行专业化时为何允许不同的返回类型
如果专业化也具有auto
占位符,则标准允许使用auto
返回类型的专业化功能
具有使用占位符类型的已声明返回类型的函数或函数模板的重新声明或特化也应使用该占位符,而不是推导类型
http://eel.is/c++draft/dcl.spec.auto#11
因此,凭借符合标准的专业化(类似于给定的示例之一),并且在任何允许的地方都没有明确禁止。
至于bar
的错误,标准说返回类型是函数模板签名的一部分:
“功能模板”的名称,参数类型列表([dcl.fct]),封闭的名称空间(如果有的话), 返回类型 ,模板头和尾随的要求子句([dcl.decl])(如果有的话)
http://eel.is/c++draft/defns.signature.templ
因此,在编译器看来, template<> int bar<0>() { return 7; }
template<> int bar<0>() { return 7; }
是template<... N> int bar();
的特殊化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
那样干净。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.