c++ - 返回类型与自动的模板特化和显式规范

标签 c++ templates

考虑代码:

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'

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

               ^

:7:26: note: candidate template ignored: could not match 'void ()' against 'int ()'

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

                     ^

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'

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

          ^

:5:28: error: template-id 'foo<0>' in declaration of primary template

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

                        ^

:7:26: error: too many template-parameter-lists

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

                      ^~~

:8:14: error: explicit specialization in non-namespace scope 'class Test'

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

          ^

:8:20: error: expected ';' at end of member declaration

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

                ^~~

                   ;

:8:23: error: expected unqualified-id before '<' token

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

                   ^

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

但是,这些代码是我找到的代码的简化版本 elsewhere ,所以也许我的解释是不正确的 - 在这种情况下,我将不胜感激解释 为什么当 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 上出错特化。

why different return type is allowed when auto is used for specialization

标准允许使用 auto 来专门化函数如果特化也有 auto 则返回类型占位符

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

因此,在编译器眼中template<> int bar<0>() { return 7; }template<... N> int bar(); 的特化模板(注意返回类型)。但它之前没有声明(特化不能先于声明)所以编译失败!如果添加 template<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 一起专门化.但它仍然不会像使用 auto 那样干净。 .

关于c++ - 返回类型与自动的模板特化和显式规范,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52462111/

相关文章:

c++ - C++11 中 "auto var = {condition} ? 1 : 1.0"的类型是什么?它是 double 还是整数?

c++ - 如何在 Debug模式下使用 Netbeans 编译 Assimp?

c++ - 以多模板类中的模板为条件

templates - Scala 模板在 Play 2.0 中访问模型对象属性

c++ - Qt QTreeWidget 替代 IndexFromItem?

c++ - 在 if-else block 中实现类似 SFINAE 的效果

c++为什么这个循环有缺陷以将数据读入结构数组?

c++ - 在 C++ 中定义多个组合对象的最佳方式?

C++ 模板 "class type"错误

azure - 在 Azure ARM 部署模板中复制复制元素内的元素以创建资源的多个实例