当前位置: 首页 > 知识库问答 >
问题:

用decltype结尾的返回类型专门化函数模板

卞轶
2023-03-14

在C 11中,如何专门化一个函数模板,该模板使用decltype声明为“复杂”的尾部返回类型?以下内容适用于GCC,但在VC2013中产生了“错误C2912:显式专业化‘int f(void)’i不是功能模板的专业化”:

#include <html" target="_blank">iostream>

int myint() { return 1; }

template<class T>
auto f() -> decltype(myint()) // this seems to cause problems
{
    std::cout << "general\n";
    return 1;
}

template <>
auto f<double>() -> decltype(myint())
{
    std::cout << "special\n";
    return 2;
}

int main()
{
    f<int>();
    f<double>(); // compiler error in VC, but not in GCC
}

我说“复杂”是因为缺乏一个技术上精确的词,因为我不确定是什么造成了差异。例如,以下decltype使用不依赖于任何函数结果类型的内置操作,可以与模板专业化配合使用:

自动f()-

所以,我的问题(都彼此相关):

  1. 我的代码正确吗C 11?
  2. 这是VC错误吗?
  3. 如果这种专门化不起作用,我怎么能专门化std::begin和std::end(从而为不可更改的遗留容器类提供基于范围的for循环)?

共有1个答案

乐正镜
2023-03-14

我的代码正确吗?

对我来说看起来是正确的。此外,使用gcc和使用-Wall-W额外的clang进行干净的编译。

这是VC漏洞吗?

很有可能。VC在这方面臭名昭著,请参阅例如Microsoft Visual C的两阶段模板实例化到底“破坏”了什么?或google msvc两阶段查找。

如果这种专门化不起作用,我怎么能专门化std::begin和std::end(从而提供基于范围的循环)来处理一个不可更改的遗留容器类呢?

对于您提供的代码,解决方法是使用typedef:

#include <iostream>

int myint() { return 1; }

typedef decltype(myint()) return_type;

template<class T>
return_type f()
{
    std::cout << "general\n";
    return 1;
}

template <>
return_type f<double>()
{
    std::cout << "special\n";
    return 2;
}

int main()
{
    f<int>();
    f<double>();
}

所有三个主流编译器(gcc、clang、vs)似乎都对这段代码感到满意。

更新:

如果这种专业化不起作用,我怎么能为不可更改的遗留容器类专门化std::beginstd::end(从而提供基于范围的循环)?
[和评论:]我认为专门化std::beginstd::end始终是最好的方法。

在仔细考虑之后,我最后的办法就是专门研究std::begin()std::end()。我的第一次尝试是提供成员begin()end()函数;不幸的是,这不是您的选项,因为您无法修改相应的代码。然后,我的第二次尝试是在我自己的命名空间中提供免费函数:

#include <iostream>
#include <initializer_list>
#include <vector>

namespace my_namespace {

template <typename T> class my_container;
template <typename T> T* begin(my_container<T>& c);
template <typename T> T* end(my_container<T>& c);

template <typename T>
class my_container {

  public:

    explicit my_container(std::initializer_list<T> list) : v(list) { }

    friend T* begin<>(my_container& c);
    friend T* end<>(my_container& c);

  private:

    std::vector<T> v;
};

template <typename T>
T* begin(my_container<T>& c) {

  return c.v.data();
}

template <typename T>
T* end(my_container<T>& c) {

  return c.v.data()+c.v.size();
}

}

int main() {

  my_namespace::my_container<int> c{1, 2, 3};

  for (int i : c)
    std::cout << i << '\n';
}

如果您能够专门化容器的std::begin()std::end(),那么这种方法必须有效。如果在全局名称空间中执行,它也可以工作(也就是说,只需省略名称空间my_namespace{并关闭}),但我更喜欢将实现放在自己的名称空间中。

参见也

>

  • 如何使我的自定义类型与“基于范围的循环”一起工作?

    为什么range-for没有找到std::istream_iterator的开始和结束重载?

  •  类似资料:
    • 我想编写一个函数模板,它返回各种类型的随机变量(bool、char、short、int、float、double,以及这些变量的无符号版本)。 我看不出如何使用最新的C 11标准库来实现这一点,因为我需要使用统一的int分布或统一的real分布。我想我可以专门化模板: 在Visual Studio 2012 Update 3下,这给出了: 错误C2668:“‘匿名命名空间’::randomPrim

    • 我试图调用一个模板类成员函数专门化,它从类的构造函数中返回一个值,但我似乎找不到正确的语法(如果存在的话)。下面是我的代码,下面是来自编译器(而不是链接器)的错误消息。 错误消息:g++-std=c++11-o t1 t1.cpp t1.cpp:19:18:错误:在'constexpr'模板constexpr const char*name()之前需要'<';^T1.CPP:22:26:错误:“c

    • 我在读一篇关于“新”C特性的文章时,遇到了decltype和它的用法。我理解decltype后面的原因,比如后面的返回类型 没有它,编译器将无法派生模板函数的返回类型。但是为什么语法是这样的呢? 为什么不使用 这会让人感觉更“自然”,因为返回类型是在正常情况下声明的,尽管这是两个参数的结果。这是因为它与其他东西冲突,还是因为如果语法是不值得的,它会给编译器带来额外的工作?

    • 我有一些代码使用两种不同类型的颜色,每个通道8位和每个通道16位,每个都由一个结构表示。为了有效地重用我的代码,我有一个模板函数可以对它们进行一些渲染。因此,我希望有一个模板函数来获取我的颜色通道的最大值。 我最初的尝试是这样的。我只展示了8 bpc的专业化 这不会在Visual studio 2012中编译。我明白了 1个 对我来说,我觉得这应该行得通,但我一直找不到任何例子。在Speciali

    • 我有一个通用算法,需要访问其模板类型的特征。有一个特征类可以专门用于提供这些特征。 在我的类中使用此算法时,我想将其与类中定义的私有类型一起使用。 然而,专门化只能发生在或全局范围内,而我的类是不可访问的。 是否有可能以某种方式专门化具有私有类型的模板,至少在可访问此类型的范围内? 也许可以将这个专门化声明为一个类?

    • 考虑代码: 我用不同的编译器(通过编译器资源管理器)测试了代码。 如果Clang 7.0.0编译,而给出错误: :8:20:错误:没有与函数模板专用化“栏”匹配的函数模板 :7:26:注意:已忽略候选模板:无法将'void()'与'int()'匹配 Visual C同意(MSVC 19 2017 RTW): (8) :错误C2912:显式专门化“int Test::bar(void)”不是函数模板