【问题标题】:How to specialise template method with type that itself is a template where only the return type relies on the template type?如何使用本身是模板的类型专门化模板方法,其中只有返回类型依赖于模板类型?
【发布时间】:2017-02-13 08:26:45
【问题描述】:

我想专门化非模板类中的单个模板方法以使用 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 是好的。

【问题讨论】:

  • 喜欢this 吗?我没有得到你期望的语法。
  • 你不能在 C++ 中部分特化函数 - 所以template&lt;T&gt; std::vector&lt;T&gt; 是不允许的

标签: c++ templates


【解决方案1】:

您只能部分特化类(结构)(cppreference)-因此解决问题的方法是添加辅助结构以允许 std::vector&lt;T&gt; 的这种部分特化-例如这样:

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&lt;T&gt;,你必须专门化整个结构:

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

【讨论】:

  • 挑剔:GetImpl 应该是私有的
  • @Caleth - 当然可以,一般情况下它应该是私有的。
【解决方案2】:

Partial specialisationtemplate 函数(包括成员函数)是不允许的。一种选择是使用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&lt;specific_type, specific_allocator&gt; 来使这个助手类更加通用。例如,

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

【讨论】:

    【解决方案3】:

    您不能在 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>>();
    }
    

    编辑:修正了代码中的一个错字

    【讨论】:

    • @PiotrSkotnicki 提出的真实标签看起来更干净并且可以处理更多类型(作为参考)。
    • 关于处理更多类型,这两个方法都处理所有 T 和 std::vector 同时允许特定的类型特化。你能详细说明为什么它看起来更干净吗?
    • 你不处理int&amp;,并且指针是常规类型(例如可以被尊重)。 tag&lt;T&gt; 是专用的。
    猜你喜欢
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    相关资源
    最近更新 更多