考虑一下代码:
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: error: no function template matches function template
specialization ‘bar’06001
:7:26: note: candidate template ignored: could not match ‘void
()’ against ‘int ()’06002
Visual C同意(MSVC 19 2017 RTW):
(8): error C2912: explicit specialization ‘int
Test::bar(void)’ is not a specialization of a function template
gcc 8.2不编译任何代码(虽然原因可能是bug in C++17 support:
:5:14: error: explicit specialization in non-namespace scope
‘class Test’06003
:5:28: error: template-id ‘foo<0>’ in declaration of primary
template06004
:7:26: error: too many template-parameter-lists
06005
:8:14: error: explicit specialization in non-namespace scope
‘class Test’06006
:8:20: error: expected ‘;’ at end of member declaration
06007
:8:23: error: expected unqualified-id before ‘<‘ token
06008
这里的正确解释是什么?我可以为不同的方法专业化使用不同的返回类型(为什么只有auto,但不能明确指定它们)?由于我对汽车和模板的理解有限,我会说“不”.我不明白为什么使用auto而不是显式命名返回类型允许不同的特殊化具有不同的返回类型.
但是,这些代码是我在elsewhere找到的代码的简化版本,所以也许我的解释是不正确的 – 在这种情况下,我会很感激解释为什么在auto用于特化时允许不同的返回类型,同时明确命名这种类型似乎是被禁止的.
最佳答案 示例代码有几个不同的问题.
1)GCC未能实现CWG 727(在C 17中要求):https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282导致错误:非命名空间范围’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专业化上出错.
why different return type is allowed when auto is used for specialization
如果特化还具有自动占位符,则标准允许使用自动返回类型来专门化函数
Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type
http://eel.is/c++draft/dcl.spec.auto#11
因此,这种专业化符合标准(类似于给定示例之一)并且在任何允许的地方都没有明确禁止.
至于bar的错误,标准说返回类型是函数模板签名的一部分:
⟨function template⟩ name, parameter type list ([dcl.fct]), enclosing namespace (if any), return type, template-head, and trailing requires-clause ([dcl.decl]) (if any)
http://eel.is/c++draft/defns.signature.templ
因此,在编译器的眼睛模板中<> int bar< 0>(){return 7; }是模板的专业化< … N> int bar();模板(注意返回类型).但它之前未声明(专业化不能在声明之前),因此编译失败!如果添加模板< int N> int bar();然后它会编译(但如果你试图调用bar,则抱怨调用歧义).
基本上,你不能在专业化中改变函数签名,你只能专门化(duh)模板参数(它也应该与声明中的完全相同,因为它也是签名的一部分).
Can I have a template specialization with explicitly declared return type different from the base template at all
如上所述 – 您无法更改功能模板签名,这意味着您无法更改返回类型.但是,如果它取决于模板参数,您可以专门化返回类型!
考虑
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一起特化.但它仍然不会像使用汽车一样干净.