使用 decltype 尾随返回类型专门化函数模板 [英] Specialize function template with decltype trailing return type

查看:40
本文介绍了使用 decltype 尾随返回类型专门化函数模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#include int myint() { 返回 1;}模板自动 f() ->decltype(myint())//这似乎会导致问题{std::cout <<"一般\n";返回 1;}模板 <>auto f<double>() ->声明类型(myint()){std::cout <<"特殊\n";返回2;}int main(){f();f<double>();//VC 中的编译器错误,但 GCC 中没有}

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

自动 f() -> decltype(1 + 1)

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

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

解决方案

我的代码在 C++11 中正确吗?

在我看来是正确的.此外,使用 -Wall -Wextra 使用 gcc 和 clang 进行干净编译.

<块引用>

这是一个 VC 错误吗?

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

<块引用>

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

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

#include int myint() { 返回 1;}typedef decltype(myint()) return_type;模板返回类型 f(){std::cout <<"一般\n";返回 1;}模板 <>return_type f(){std::cout <<"特殊\n";返回2;}int main(){f();f<double>();}

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

<小时>

更新:

<块引用>

我怎么可能专门化std::beginstd::end(并因此提供基于范围的 for 循环)用于不可更改的如果这种专业化不起作用,则遗留容器类?
[来自评论:]我认为专门化 std::beginstd::end 总是最好的方法.

经过深思熟虑,专攻std::begin()std::end() 将是我最后的手段.我的第一次尝试是提供成员 begin()end() 函数;不幸的是,它不是您的选择,因为您无法修改相应的代码.然后,我的第二次尝试是在我自己的命名空间中提供免费的函数:

#include #include <initializer_list>#include <向量>命名空间 my_namespace {模板 类 my_container;模板 T* 开始(my_container<T>& c);模板 T* end(my_container<T>&c);模板 类 my_container {民众:显式 my_container(std::initializer_list<T> list) : v(list) { }朋友 T* begin<>(my_container& c);朋友T*结束<>(my_container&c);私人的:std::vectorv;};模板 T* 开始(my_container<T>& c){返回 c.v.data();}模板 T* 结束(my_container<T>& c){返回 c.v.data()+c.v.size();}}int main() {my_namespace::my_containerc{1, 2, 3};for (int i : c)std::cout <<我<<'\n';}

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

另见

In C++11, how can I specialise a function template which is declared with a "complicated" trailing return type using decltype? The following works in GCC but produces "error C2912: explicit specialisation 'int f(void)' i s not a specialisation of a function template" in 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
}

I say "complicated" in lack of a technically precise word because I'm not sure what makes the difference. For example, the following decltype, using a built-in operation not depending on any function result type, works fine with template specialisation:

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

So, my questions (all related to each other):

  1. Is my code correct C++11?
  2. Is this a VC bug?
  3. How could I ever specialise std::begin and std::end (and thus provide range-based for loops) for an unchangeable legacy container class if this kind of specialization does not work?

解决方案

Is my code correct C++11?

Looks correct to me. Also, compiles cleanly both with gcc and clang with -Wall -Wextra.

Is this a VC bug?

Most likely. VC is infamous in this respect, see for example What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation? or google msvc two-phase lookup.

How could I ever specialise std::begin and std::end (and thus provide range-based for loops) for an unchangeable legacy container class if this kind of specialization does not work?

For the code you provided, a workaround would be to use a 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>();
}

All three mainstream compilers (gcc, clang, vs) seem to be happy with this code.


UPDATE:

How could I ever specialise std::begin and std::end (and thus provide range-based for loops) for an unchangeable legacy container class if this kind of specialization does not work?
[And from the comments:] I thought specialising std::begin and std::end was always the best approach.

After giving it some thought, specializing std::begin() and std::end() would be my last resort. My first attempt would be to provide member begin() and end() functions; unfortunately, it is not an option for you because you cannot modify the corresponding code. Then, my second attempt would be to provide free functions in my own namespace:

#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';
}

This approach must work if you were able to specialize std::begin() and std::end() for the container. It also works if you do it in the global namespace (that is, you simply omit the namespace my_namespace { and closing }) but I prefer to put my implementation into my own namespace.

See also

这篇关于使用 decltype 尾随返回类型专门化函数模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆