Specialize function template with decltype trailing return type

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)' is 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 << "generaln";
    return 1;
}

template <>
auto f<double>() -> decltype(myint())
{
    std::cout << "specialn";
    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):

  • Is my code correct C++11?
  • Is this a VC bug?
  • 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 << "generaln";
        return 1;
    }
    
    template <>
    return_type f<double>()
    {
        std::cout << "specialn";
        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

  • How to make my custom type to work with "range-based for loops"?

  • Why doesn't range-for find my overloads of begin and end for std::istream_iterator?

  • 链接地址: http://www.djcxy.com/p/66502.html

    上一篇: 使用auto和decltype从模板类中的函数返回引用

    下一篇: 专用具有decltype追踪返回类型的函数模板