具有decltype尾随返回类型的Specialize函数模板

Chr*_*ckl 1 c++ decltype template-specialization visual-c++ c++11

在C++ 11中,如何使用decltype专门化一个使用"复杂"尾随返回类型声明的函数模板?以下在GCC中工作,但在VC2013中产生"错误C2912:显式特化'int f(void)'不是函数模板的特化":

#include <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
}
Run Code Online (Sandbox Code Playgroud)

我说缺乏一个技术上精确的单词"复杂",因为我不确定是什么产生了影响.例如,使用不依赖于任何函数结果类型的内置操作的以下decltype 适用于模板特化:

auto f() - > decltype(1 + 1)

所以,我的问题(都是相互关联的):

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

Ali*_*Ali 5

我的代码是否正确C++ 11?

看起来对我不对.此外,使用gcc和clang编译干净-Wall -Wextra.

这是VC的错误吗?

最有可能的.VC在这方面臭名昭着,例如参见Microsoft Visual C++的两阶段模板实例化究竟是什么"打破"?google msvc两阶段查找.

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

对于您提供的代码,解决方法是使用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>();
}
Run Code Online (Sandbox Code Playgroud)

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


更新:

我怎么都专门std::beginstd::end (并因此提供基于范围for循环)的一个不可改变的遗留容器类,如果这种专业化不起作用?
[并从评论:] 我认为专业std::begin,std::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';
}
Run Code Online (Sandbox Code Playgroud)

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

也可以看看


归档时间:

查看次数:

1549 次

最近记录:

12 年,3 月 前