c – 返回类型与自动的模板特化和显式指定

考虑一下代码:

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
template

06004

: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一起特化.但它仍然不会像使用汽车一样干净.

点赞