如何使用自身为模板的模板方法专门化模板方法,其中只有返回类型依赖于模板类型?

时间:2017-02-13 08:26:46

标签: c++ templates

我想在非模板类中专门使用单个模板方法来使用std::vector,但只有方法的返回类型才使用模板。

#include <iostream>
#include <string>
#include <vector>

class Foo
{
public:
    template<typename T>
    T Get()
    {
        std::cout << "generic" << std::endl;
        return T();
    }
};

template<>
int Foo::Get()
{
    std::cout << "int" << std::endl;
    return 12;
}

template<typename T>
std::vector<T> Foo::Get()
{
    std::cout << "vector" << std::endl;
    return std::vector<T>();
}

int main()
{
    Foo foo;
    auto s = foo.Get<std::string>();
    auto i = foo.Get<int>();
}

这会编译错误,表明std::vector尝试的专业化与Foo的任何原型都不匹配,这是完全可以理解的。

如果重要的话,使用C ++ 14就可以了,花花公子。

3 个答案:

答案 0 :(得分:4)

你只能部分地专门化类(结构)(cppreference) - 所以克服你的问题的方法是添加辅助结构以允许std::vector<T>的这种部分特化 - 例如这样:

class Foo
{
private: // might be also protected or public, depending on your design
    template<typename T>
    struct GetImpl
    {
        T operator()()
        {
            std::cout << "generic" << std::endl;
            return T();
        }
    };
public:
    template<typename T>
    auto Get()
    {
        return GetImpl<T>{}();
    }
};

对于int - 你可以完全专注于这个功能:

template<>
int Foo::GetImpl<int>::operator()()
{
    std::cout << "int" << std::endl;
    return 12;
}

对于std::vector<T>,您必须专门化整个结构:

template<typename T>
struct Foo::GetImpl<std::vector<T>>
{
    std::vector<T> operator()()
    {
        std::cout << "vector" << std::endl;
        return std::vector<T>();
    }
};

答案 1 :(得分:3)

Partial specialisation template个函数(包括成员函数)是不允许的。一种选择是使用SFINAE重载。例如,

/// auxiliary for is_std_vetor<> below
struct convertible_from_std::vector
{
    template<typename T> 
    convertible_from_std::vector(std::vector<T> const&); 
};

template<typename V>
using is_std_vector
    = std::is_convertible<V,convertible_from_std_vector>;

class Foo
{
public:
    template<typename T, std::enable_if_t< is_std::vector<T>::value,T>
    Get()
    {
        std::cout << "vector" << std::endl;
        return T();
    }
    template<typename T, std::enable_if_t<!is_std::vector<T>::value,T>
    Get()
    {
        std::cout << "generic" << std::endl;
        return T();
    }
};

请注意,辅助类is_std_vector在其他上下文中也很有用,所以值得拥有它。进一步注意,通过要求任何std::vector或特定std::vector<specific_type, specific_allocator>,您可以使此助手类更具通用性。例如,

namespace traits {
    struct Anytype {};
    namespace details {
        /// a class that is convertible form C<T,T>
        /// if either T==AnyType, any type is possible
        template<template<typename,typename> C, typename T1=Anytype,
                                                typename T2=Anytype>
        struct convCtTT
        {
            convCtTT(C<T1,T2> const&);
        };

        template<template<typename,typename> C, typename T1=Anytype>
        struct convCtTT<C,T1,AnyType>
        {
            template<typename T2>
            convCtTT(C<T1,T2> const&);
        };

        template<template<typename,typename> C, typename T2=Anytype>
        struct convCtTT<C,AnyType,T2>
        {
            template<typename T1>
            convCtTT(C<T1,T2> const&);
        };

        template<template<typename,typename> C>
        struct convCtTT<C,AnyType,AnyType>
        {
            template<typename T1, typename T2>
            convCtTT(C<T1,T2> const&);
        };
    }
    template<typename Vector, typename ValueType=AnyType,
                              typename Allocator=AnyType>
    using is_std_vector
      = std::is_convertible<Vector,details::convCtTT<std::vector,ValueType,
                                                                 Allocator>;
}

答案 2 :(得分:2)

您无法在c ++中部分特殊化模板。您需要重载函数并在参数中传递类型。

#include <iostream>
#include <string>
#include <vector>

class Foo
{
public:
    template<typename T>
    T Get()
    {
       return  this->getTemplate(static_cast<T*>(0)); // 
    }
private:
    template<class T> T getTemplate(T* t)
    {
         std::cout << "generic" << std::endl;
         return T();
    }
    template<class T> std::vector<T> getTemplate(std::vector<T>* t)
    {
         std::cout << "vector" << std::endl;
         return std::vector<T>();
    }
};

template <> int Foo::getTemplate(int* t)
{
    std::cout << "int" << std::endl;
    return 12;
}
int main()
{
    Foo foo;
    auto s = foo.Get<std::string>();
    auto i = foo.Get<int>();
    auto v = foo.Get<std::vector<int>>();
}

编辑:修复了代码中的拼写错误