首页 文章

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

提问于
浏览
1

在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
}

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

auto f() - > decltype(1 1)

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

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

  • 这是一个VC错误吗?

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

1 回答

  • 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)似乎对这段代码感到满意 .


    UPDATE:

    如果这种专业化不起作用,我怎么能专门化std :: begin和std :: 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';
    }
    

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

    也可以看看

相关问题