Reputation: 1
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
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 c++17 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
Reputation: 49
Attempting to answer various levels of complexity and I'll presume a C++11 sort of answer.
inline std::string test(bool b) { return b ? std::string("true") : std::string("false"); }
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.
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