【问题标题】:Overload C++ template class method by it's template classes通过它的模板类重载 C++ 模板类方法
【发布时间】:2021-10-01 00:00:15
【问题描述】:

我正在编写一个模板类来管理 2 个类的联合。除了Get() 函数之外,所有函数都非常简单。它看起来像这样:

UnionPair.hpp

template <class First, class Second>
class UnionPair {
 public:
  UnionPair() : state_(State::kEmpty){};
  ~UnionPair(){};

  void Reset();

  void Set(std::unique_ptr<First>&& first);
  void Set(std::unique_ptr<Second>&& second);

  template <First>
  First *Get();

  template <Second>
  Second *Get();

 private:
  enum class State { kEmpty, kFirst, kSecond } state_;
  union {
    std::unique_ptr<First> first_;
    std::unique_ptr<Second> second_;
  };
};

UnionPair.cpp

template <class First, class Second>
template <First>
First *UnionPair<First, Second>::Get() {
  return first_.get();
}

template <class First, class Second>
template <Second>
Second *UnionPair<First, Second>::Get() {
  return second_.get();
}

我正在尝试使 Get() 函数模板方法只能使用其实例化的 UnionPair 对象的模板类调用,以便它们可以被调用它们的模板类重载。以上代码无法编译。

我对如何调用它的想法是这样的,但是当我尝试调用它们时出现编译器错误:

// Definitions of structs A, B and C here

UnionPair<A, B> pair;
pair.Set(std::unique_ptr(new A()));
pair.Get<A>(); // should return a pointer to A (causes a compiler error right now)
pair.Get<C>(); // should cause a compiler error

我不确定如何在 .cpp 文件中实现此方法,或者是否可行。我一直在阅读有关模板内的专业化的文章,但没有看到任何与我的类似的示例,或者专业化类必须与对象的模板类相同的示例。

我的用法/定义有什么问题?我正在尝试做的事情可能吗?如果没有,有没有其他选择?我不想做的是像First *GetFirst() 这样为 First 和 Second 分别设置一个 getter,因为这依赖于对象实例化中类的顺序并且不清楚。

旁注:如果有一个 c++ 库来管理工会成员的状态、设置和获取,我会考虑使用它,但我仍然想弄清楚我的问题。

【问题讨论】:

  • 处理这个问题的 C++ 标准库类模板是std::variant
  • 有趣。看起来这就是我要找的东西,但我认为如果您尝试从中获取错误的类型,它会引发运行时错误?这是否意味着没有办法得到编译器错误?我也不能在我的项目中使用 c++ 17,所以我不能使用这个类。但是感谢您指出!
  • 像这样将模板代码放入.cpp 文件是行不通的。您犯了一个非常常见的错误,即预先编写大量代码,然后才试图使一切正常。专业的 C++ 开发人员不会那样工作。他们只写了几行代码,编译,测试,确保它们工作,然后再写几行。你应该从一个只有几行代码的模板开始,让所有这些工作,然后再写几行模板代码。
  • 我真的不明白我写的这 30 行代码是如何“大堆”的......
  • 我不认为当使用错误的 A 或 B 之一时您将能够得到编译器错误,因为编译器可能不知道在构造对象后设置了哪个,但是使用 C 时会出现编译器错误 — get&lt;C&gt;(variant&lt;A,B&gt;) 应该是错误。

标签: c++ class templates


【解决方案1】:

首先,您不能将模板代码拆分为单独的 .h.cpp 文件:

Why can templates only be implemented in the header file?

其次,union 的数据直到运行时才设置,因此编译器无法在编译时验证Get() 的模板参数,至少不是您想要的方式。只能在编译时验证联合是否可以永远转换为指定的类型,但如果它可以转换,那么你无法验证,直到运行时,在分配了union 之后。

试试这样的:

#include <memory>
#include <type_traits>

template <class First, class Second>
class UnionPair {
 static_assert(!std::is_same<First, Second>::value, "First and Second can't be the same type");

 public:
  UnionPair();
  UnionPair(std::unique_ptr<First>&& value);
  UnionPair(std::unique_ptr<Second>&& value);
  ~UnionPair(){}

  void Reset();

  void Set(std::unique_ptr<First>&& value);
  void Set(std::unique_ptr<Second>&& value);

  template <class T>
  T* Get();

 private:
  enum class State { kEmpty, kFirst, kSecond } state_;

  union {
    std::unique_ptr<First> first_;
    std::unique_ptr<Second> second_;
  };

  template<class T>
  typename std::enable_if<std::is_same<T, First>::value, T*>::type
    InternalGet();

  template<class T>
  typename std::enable_if<std::is_same<T, Second>::value, T*>::type
    InternalGet();
};

template <class First, class Second>
UnionPair<First, Second>::UnionPair()
    : state_(State::kEmpty)
{
}

template <class First, class Second>
UnionPair<First, Second>::UnionPair(std::unique_ptr<First>&& value)
    : UnionPair()
{
    Set(std::move(value));
}

template <class First, class Second>
UnionPair<First, Second>::UnionPair(std::unique_ptr<Second>&& value)
    : UnionPair()
{
    Set(std::move(value));
}

template <class First, class Second>
void UnionPair<First, Second>::Reset()
{
  if (state_ == State::kFirst)
    first_.reset();
  else if (state_ == State::kSecond)
    second_.reset();
  state_ = State::kEmpty;
}

template <class First, class Second>
void UnionPair<First, Second>::Set(std::unique_ptr<First>&& value)
{
    Reset();
    first_ = std::move(value);
    state_ = State::kFirst;
}

template <class First, class Second>
void UnionPair<First, Second>::Set(std::unique_ptr<Second>&& value)
{
    Reset();
    second_ = std::move(value);
    state_ = State::kSecond;
}

template <class First, class Second>
template <class T>
typename std::enable_if<std::is_same<T, First>::value, T*>::type
  UnionPair<First, Second>::InternalGet() {
    if (state_ == State::kFirst)
        return first_.get();
    throw std::domain_error("wrong state");
}

template <class First, class Second>
template <class T>
typename std::enable_if<std::is_same<T, Second>::value, T*>::type
  UnionPair<First, Second>::InternalGet() {
    if (state_ == State::kSecond)
        return second_.get();
    throw std::domain_error("wrong state");
}

template <class First, class Second>
template <class T>
T* UnionPair<First, Second>::Get() {
    return InternalGet<T>();
}
UnionPair<A, B> pair;
pair.Set(std::unique_ptr<A>(new A));
pair.Get<A>(); // OK
pair.Get<B>(); // runtime error
pair.Get<C>(); // compiler error

【讨论】:

  • 是的,也许我不清楚,但我完全理解它不会导致 A 和 B 之间的编译器错误。我只是问使用 C 会导致编译器错误,而您的代码会这样做。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-10-22
  • 1970-01-01
  • 1970-01-01
  • 2016-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-19
相关资源
最近更新 更多