正确的方法有条件地从模板函数返回不同的类型

时间:2016-09-13 10:17:14

标签: c++ templates

我有一个通常返回整数的函数,但由于值的语义可能不同,我想强类型那些,所以我介绍了两种类型,例如:金钱和时间,简化为

struct Money {
   uint32_t value;
}

该函数将返回Money或Time,具体取决于bool参数。让我们说它看起来像这样:

template <typename T> T getValue(bool mode) {
  Money money;
  Time time;
  ...
  if (mode == ModeMoney) {
   money = something * 2;//get it from somewhere - irrelevant for the example
   return money;
  }
  if (mode == ModeTime) {
   time = something * 100;
   return time;
  }
}

现在编译器会抱怨不同的返回类型,所以我添加了专门的模板函数来返回值本身:

template <> Money variableValue<Money>(something) { return something * 2 };
template <> Time variableValue<Time>(something) { return something * 100};

这允许在调用时删除bool param,main函数现在将更改为:

template <typename T> T getValue(bool mode) {
  ....//calculation of *something* is the same, we only need different output from the function
  return variableValue<T>(something);
}

这是一个好方法吗?

3 个答案:

答案 0 :(得分:2)

按照Ami的评论,我建议你做两个函数和一个帮手:

Time getTime() { return calculateSomething() * 100; }
Money getMoney() { return calculateSomething() * 2; }

将单独的用例分开;模板的主要目的是使接口更简单,而不是实现。但是,如果你使用模板将看起来应该是两个函数的东西变成一个,这不会使界面更简单,因为它不直观。 (也不是,正如你的问题所示,它实际上是否使实现变得更简单。)

答案 1 :(得分:1)

作为替代解决方案,您可以使用traits类来执行此操作并避免重复代码 它遵循一个最小的例子:

template<typename>
struct traits;

template<>
struct traits<Money> {
    using return_type = Money;
    static constexpr std::size factor = 2;
};

template<>
struct traits<Time> {
    using return_type = Time;
    static constexpr std::size factor = 100;
};

template <typename T>
traits<T>::return_type getValue() {
    traits<T>::return_type ret;
    // ...
    ret = something * traits<T>::factor;
    return ret:
}

如果它是一个合适的解决方案,主要取决于实际代码和getValue的实际实现。

答案 2 :(得分:0)

除了关于设计问题的问题之外,您似乎想要一个标记的联合或变体variant<Time, Money>。下一个C ++标准将有variant class in the standard library,但在此可用之前,您可以使用Boost.Variant。有了这个,代码看起来像

std::variant<Money, Time> getValue(bool mode) {
    if (mode) {
        return std::variant<Money, Time>( Money{23} );
    }
    else {
        return std::variant<Money, Time>( Time{42} );        
    }
}

auto m = std::get<Money>( getValue(true) );
auto m2 = std::get<Money>( getValue(false) );
assert( m != nullptr && m2 == nullptr );

您也可以仅使用union实现自己的Money和Time变体:

struct TimeOrMoney
{
    enum class Type {isTime, isMoney};
    Type type;
    union
    {
        Time time;
        Money money;
    }
}

另一种方法可能是返回std::pair< boost::optional<Money>, boost::optional<Time> >,然后检查两者中的哪一个实际设置。这使用特殊的可选状态作为标记来指示已设置的值。