2016-09-13 3 views
1

私は通常整数を返す関数を持っていますが、値の意味が違うかもしれないので、それらを強く型付けしたいと思います。お金と時間は、テンプレート関数からさまざまな型を条件付きで返すための正しいアプローチ

struct Money { 
    uint32_t value; 
} 

のように簡略化されます。関数は、ブールパラメータに応じてMoneyまたはTimeのいずれかを返します。今コンパイラが異なる戻り値の型のために文句を言うだろう

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

これはブール値をドロップすることができますparam on invocationと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); 
} 

これは良いアプローチですか?

+6

関数のロジックと戻り値の型が異なる場合は、2つの関数に分割してみましょう。 –

+0

ユースケースとは何ですか? 'getValue'の使い方によっては、もっと良いかもしれないいくつかの選択肢があります。 – wasthishelpful

+0

私が投稿した例は非常に単純化されており、混乱の元になるかもしれません。ロジックは実際には同じですが、2つの関数を書くと、ロジックが2つの異なる出力で複製されます – estoy

答えて

1

traitsクラスを使用してコードを複製しないようにすることもできます。それは、適切な解決策は、ほとんどが実際のコードとgetValueの実際の実装に依存していた場合

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


それは、最小限の例に従います。

2

は亜美さんのコメントに続いて、私は次の2つの関数とヘルパーを作るお勧めします。

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

は別の独立したユースケースを保管してください。テンプレートの主な目的は、インターフェイスを実装するのではなく、簡単にすることです。しかし、テンプレートを使って2つの関数を1つの関数にするようなものにすると、インターフェイスが単純になりません。なぜなら、直感的ではないからです。 (あなたの質問に示されているように、実際には実装が簡単になりません。)

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

のように見えるあなたはまた、唯一の組合を利用してお金と時間のために、独自のバリアントを実装することができます

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

代替std::pair< boost::optional<Money>, boost::optional<Time> >を返し、その後、どのチェックすることであってもよいですの2つが実際に設定されます。これは、特別な任意の状態をタグとして使用して、どの値が設定されているかを知らせます。

+0

ああ、そうです! std :: variantは最も洗練されたソリューションでした。まだ利用可能ではなく、ブーストを使用することはできません。一方、 では、struct + unionのアイデアはきちんとしているようです – estoy

関連する問題