Psul
Psul

Reputation: 1

How do I use C++ template tags in if statements

Consider this class:

template <bool true_or_false>
class Test
{
  std::string test();
};

template<>
std::string Test<true>::test()
{
  return std::string{"true"};
}

template<>
std::string Test<false>::test()
{
  return std::string{"false"};
}

Obviously this is a trivial example, but what if I write it like this:

template <bool true_or_false>
class Test
{
  std::string test()
  {
    if (true_or_false)
    {
      return std::string{"true"};
    }
    return std::string{"false"};
  }
};

All the values are known at compile time so the compiler should be able to generate exactly the same code in each case but the second version is shorter and (to me at least) easier to read. I haven't seen any examples of such use of template tags anywhere else and it looks a little "off". Does anyone have any good reason why I should stick with the first version or switch to the second?

Upvotes: 0

Views: 333

Answers (2)

Bitwize
Bitwize

Reputation: 11220

As a general rule, it's better to write clear, concise, and maintainable code. In that regard, the second approach will be better for readability since it indicates a conditional nature of the logic.

In or above, it would actually be recommended to do this using if constexpr, which both clearly indicates the conditional nature of the code while also ensuring that no extra branches will be generated:

template <bool true_or_false>
class Test
{
  std::string test()
  {
    if constexpr (true_or_false) {
      return std::string{"true"};
    } else {
      return std::string{"false"};
    }
  }
};

Which, in terms of readability, will be much more clear than defining partial-specializations for each input.


Writing template specializations can also accomplish the same goal, but it is much easier to make mistakes such as forgetting to define a specialization may lead to a link error somewhere down the line. In the event that more template arguments than just a simple bool exist, this also gets more confusing on the intent (is this simply conditional logic, or something more?).

Using template specializations for this purpose can also have a higher barrier of entry for potential maintainers of a project, if this is code used in a team-environment. Ultimately I would recommend avoiding this approach unless you absolutely had to.

Upvotes: 1

Rinzler
Rinzler

Reputation: 49

Attempting to answer various levels of complexity and I'll presume a C++11 sort of answer.

  1. If you already know the boolean value prior to calling this function I'd vote you use an inline function, let the compiler do the optimization for you and then your lexical personalization can be contained. A metafunction would likely be overkill in this simple situation.
inline std::string test(bool b) { return b ? std::string("true") : std::string("false"); }
  1. If you don't have the boolean value 'on-hand' prior, then it might be a result of a metafunction invoked on an object or just be a value of the class that you don't care to require the user to reach in and find. If this is the case, then the typical approach is to provide a templated function that takes the object that represents the type you want to dig into, allow that function to deduce the type for you (preventing the user of your function from stating the type explicitly), and then invoke whatever metafunction or query whatever type-property you planned on extracting in the first place. In a situation this simple again, I'd just vote to stop at the function template.
template < typename T >
std::string test(const T& /* no need for a variable name, we'll not be using the variable itself */)
{
    if(std::is_enum<T>::value || T::C_ConstexprValue)
        return "true";
    return "false";
}

Note that this method requires you to provide the type to the function so that deduction can be done. This should not be considered an issue or a burden, but it is worth noting.

  1. If your boolean is a conjunction of multiple properties or values, then you'd finally have good reason to write a metafunction. Templated functions cannot be partially specialized, but class templates can. In this case your various properties would be reflected in the partial specializations you implement.
template < bool T_IsEnum, bool T_CustomProperty > 
struct Test
{
    static std::string test() { return "false"; }
};

template < >
struct Test<true, false>
{
    static std::string test() { return "partially-true"; }
};

template < >
struct Test<false, true>
{
    static std::string test() { return "partially-true"; }
};

template < >
struct Test<true, true>
{
    static std::string test() { return "true"; }
};

template < typename T >
std::string test(const T& t)
{
    // Use this function to do the type-deduction for you, then do the
    // messy type queries and such here, preventing the consumer of this
    // function from needing to do so.
    return Test<std::is_enum<T>::value, T::C_ConstValue>::test();
}

All examples intend for you to call the test(...) function. While these are just some generic answers, more specific questions can have more specific answers, but it all depends on the experience you want to create for the caller of your function.

It's worth also mentioning that we assume that the type you are referring to in all of these cases can be constructed, etc. More intricate assumptions like this are overlooked currently until the question calls for such an answer.

Upvotes: 0

Related Questions