如何在类外声明专门化模板方法?

时间:2017-10-02 13:59:02

标签: c++ templates visual-c++

Converter类旨在将字符串转换为另一种类型。因此,如果目标也是一个字符串,那么它应该只返回原始值的副本。

工作代码是这样的:

template<typename C>
class Converter {
public:
    template<typename T>
    static T To(const std::wstring& obj);

    template<>
    static std::wstring To(const std::wstring& obj) {
        return obj;
    }
};

template<typename C>
template<typename T>
T Converter<C>::To(const std::wstring& obj) {
    // Conversion
    return T();
}

(适用于VC ++ 2015 Update 3)

我试图在类声明之外移动特化方法,如下所示:

template<typename C>
template<>
std::wstring Converter<C>::To<std::wstring>(const std::wstring& obj) {
    return obj;
}

有几个编译错误:

  

错误C3212'Convertor :: To':a的显式特化   模板成员必须是显式专业化的成员

     

错误C2768'Converter :: To':非法使用显式模板   参数

4 个答案:

答案 0 :(得分:2)

您可以改为使用代码调度:

template <typename> struct Tag{};

template<typename C>
class Converter {

    template<typename T>
    static T To(const std::wstring& obj, Tag<T>);

    static std::wstring To(const std::wstring& obj, Tag<std::wstring>);

public:
    template<typename T>
    static T To(const std::wstring& obj) { return To(obj, Tag<T>{}); }
};

然后

template<typename C>
template<typename T>
T Converter<C>::To(const std::wstring& obj, Tag<T>) {
    // Conversion
    return T();
}

template<typename C>
std::wstring Converter<C>::To(const std::wstring& obj, Tag<std::wstring>) {
    return obj;
}

答案 1 :(得分:1)

明确禁止您尝试做的事情:

  

[temp.expl.spec] / 16 在类模板成员或名称空间作用域中出现的成员模板的显式专门化声明中,成员模板及其某些封闭类模板可能保持非专业化,除了声明如果其封闭类模板没有明确专门化,则声明不应明确专门化类成员模板 [强调我的] ... [示例:

template <class T1> class A {
  template<class T2> class B {
    template<class T3> void mf1(T3);
    void mf2();
  };
};
template <> template <class X>
  class A<int>::B {
    template <class T> void mf1(T);
  };
template <> template <> template<class T>
  void A<int>::B<double>::mf1(T t) { }

template <class Y> template <>
  void A<Y>::B<double>::mf2() { } // ill-formed; B<double> is specialized but
                                  // its enclosing class template A is not
     

- 结束示例]

答案 2 :(得分:0)

经验法则:

  

更喜欢函数重载超过特化!

在你的情况下使用重载和sfinae:

#include <type_traits>
#include <string>

template<typename C>
class Converter {
public:
    template<typename T, typename std::enable_if<!std::is_same<T, std::wstring>::value>::type* = nullptr >
    static T To(const std::wstring& obj) {

    }

    template<typename T, typename std::enable_if<std::is_same<T, std::wstring>::value>::type* = nullptr >
    static std::wstring To(const std::wstring& obj) {

    }
};

[live demo]

答案 3 :(得分:0)

c ++不支持它,来自Igor的回答是正确的。仍然有一个窍门。

但是首先-为什么真正需要它?

在我的情况下:可以说我只想通过提供模板参数的返回类型来“重载”函数,而我不想在带有大定义的标头中弄乱。

这种情况与作者不同,但是问题和解决方案是相同的。

技巧是为每个显式模板创建一个没有模板的对应方法:

// SomeClass.h
template <typename ClassType>
class SomeClass
{
public:
    template <typename ReturnType> ReturnType Get(const std::string& parameter) const;

    template <>
    inline int Get<int>(const std::string& parameter) const { return GetInt(parameter); }

    template <>
    inline float Get<float>(const std::string& parameter) const { return GetFloat(parameter); }

    template <>
    inline std::string Get<std::string>(const std::string& parameter) const { return GetString(parameter); }

private:
    int GetInt(const std::string& parameter) const;
    float GetFloat(const std::string& parameter) const;
    std::string GetString(const std::string& parameter) const;
}


// SomeClass.inl
template <typename ClassType>
int SomeClass<ClassType>::GetInt(const std::string& parameter) const { ... }

template <typename ClassType>
float SomeClass<ClassType>::GetFloat(const std::string& parameter) const { ... }

template <typename ClassType>
std::string SomeClass<ClassType>::GetString(const std::string& parameter) const { ... }

由于这些方法是私有的,因此您可以根据需要使用任意长的垃圾名称。

现在,其用法将如下所示:

SomeClass<WhateverType> a;

int count = a.Get<int>("count");
float width = a.Get<float>("width");
std::string name = a.Get<std::string>("name");