返回类型的C++11 方法模板专用化

C++11 method template specialization for return type

本文关键字:专用 方法 C++11 返回类型      更新时间:2023-10-16

>我有以下类:

class Foo {
 public:
  template <typename T>
  T bar() {
      cout << "Called with return type: " << typeid(T).name() << endl;
      T t = //... (some implementation here)
      return t;
  }
}

它按以下方式调用:

Foo foo;
int i = foo.bar<int>();
long l = foo.bar<long>();

现在,我想对使用shared_ptr<T>调用函数的情况有不同的专业化

Foo foo;
foo.bar<shared_ptr<int>>();
foo.bar<shared_ptr<long>>();

但是,当然,我不想为每种类型创建完全专业化。是否有可能实现这种行为(如果需要,可以基于特征)?

您不能部分专用化功能。有关原因的故事,请查看此GOTW。

不过,您可以

部分专业化课程,因此您可以做的是:

template <typename T>
T bar() {
    return bar_impl<T>::apply(this);
}

哪里:

template <typename T>
struct bar_impl {
    static T apply(Foo* ) {
        // whatever
    }
}
template <typename T>
struct bar_impl<std::shared_ptr<T>> {
    static std::shared_ptr<T> apply(Foo* ) {
        // whatever else
    }
}

当然有很多方法可以做到这一点。 我想到的第一种方法就是函数重载。 由于您没有要重载的参数,因此您必须创建一个。 我喜欢指针,它有效地充当将类型传递给函数的一种方式。

class Foo {
    //regular overload 
    template<typename T>
    T bar(T*) { //takes a pointer with an NULL value
      cout << "Called with return type: " << typeid(T).name() << endl;
      T t = //... (some implementation here)
      return t;
    }
    //shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR
    template<typename T>
    std::shared_ptr<T> bar(std::shared_ptr<T>*) { //takes a pointer with an null value
      cout << "Called with return type: " << typeid(T).name() << endl;
      std::shared_ptr<T> t = //... (some implementation here)
      return t;
    }
public:
    template <typename T>
    T bar() {
        T* overloadable_pointer = 0;
        return bar(overloadable_pointer);
    }
};

我从未听说过其他人使用指针来传递类型,所以如果您选择这样做,请彻底评论,只是为了安全起见。 这是奇怪的代码。

简单地使用帮助程序结构进行模板专用化可能更直观,这是大多数人会做的。 遗憾的是,如果您需要访问 Foo 的成员(您可能这样做),使用模板专用化将需要您将所有这些成员传递给函数,或者与模板帮助程序交朋友。 或者,您可以将type_traits专用化的东西传递给另一个成员,但这最终只是上面指针技巧的复杂版本。 许多人发现它更正常,不那么令人困惑,所以这里是:

template<typename T>
struct Foo_tag {};
class Foo {
    //regular overload 
    template<typename T>
    T bar(Foo_tag<T>) {
    }
    //shared_ptr overload - NOTE THAT T IS THE POINTEE, NOT THE SHARED_PTR
    template<typename T>
    std::shared_ptr<T> bar(Foo_tag<std::shared_ptr<T>>) {
    }
public:
    template <typename T>
    T bar() {
        return bar(Foo_tag<T>{});
    }
}

由于还没有人提出,可以使用SFINAE来区分Tstd::shared_ptr<U>

template <typename T>
struct is_shared_ptr_impl : std::false_type {};
template <typename T>
struct is_shared_ptr_impl<std::shared_ptr<T>> : std::true_type {};
template <typename T>
using is_shared_ptr = typename is_shared_ptr_impl<typename std::decay<T>::type>::type;
class Foo
{
public:    
    template <typename T>
    auto bar()
        -> typename std::enable_if<!is_shared_ptr<T>{}, T>::type
    {
        std::cout << "T is " << typeid(T).name() << std::endl;
        return {};
    }
    template <typename T>
    auto bar()
        -> typename std::enable_if<is_shared_ptr<T>{}, T>::type
    {
        using U = typename std::decay<T>::type::element_type;
        std::cout << "T is shared_ptr of " << typeid(U).name() << std::endl;
        return {};
    }
};

演示