在C ++ 11中,如何使用decltype专门化一个使用“复杂”尾随返回类型声明的函数模板?以下在GCC中工作但产生“错误C2912:显式特化'int f(void)'我不是VC2013中的函数模板的特化:
#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
}
由于我不确定是什么造成了不同,我说“复杂”缺乏一个技术上精确的词。例如,使用内置操作而不依赖于任何函数结果类型的以下decltype适用于模板特化:
自动f() - &gt; decltype(1 + 1)
所以,我的问题(都是彼此相关的):
答案 0 :(得分:5)
我的代码是否正确C ++ 11?
对我来说是正确的。此外,使用gcc和clang与-Wall -Wextra
完全编译。
这是一个VC错误吗?
最有可能。 VC在这方面很臭名昭着,例如参见What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation?或google msvc two-phase lookup。
如果这种专业化不起作用,我怎么能专门化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>();
}
所有三个主流编译器(gcc,clang,vs)似乎对此代码感到满意。
<强>更新强>
我怎么能专攻
std::begin
和std::end
(因此为循环提供基于范围的)不可更改的 遗留容器类,如果这种专业化不起作用?
[并且来自评论:] 我认为专注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';
}
如果您能够为容器专门化std::begin()
和std::end()
,则此方法必须有效。如果你在全局命名空间中执行它(也就是说,你只是省略namespace my_namespace {
并关闭}
),它也可以工作,但我更喜欢将我的实现放入我自己的命名空间。
另见