Collection of several C++ tricks that may help you with C++ development.
Made by Patrick Stritch.
template <typename T>
void Foo(T arg)
{
static_assert(std::is_arithmetic_v<std::remove_reference_t<T>>, "Function Foo takes only arithmetic types!");
// code that operates on arg
// if T is not arithmetic
// this part of code may throw bunch of unreadable errors
}
First approach
template <typename T>
void Foo(T arg)
{
constexpr bool condition = std::is_arithmetic_v<std::remove_reference_t<T>>;
if constexpr (!condition)
{
static_assert(condition, "Function Foo takes only arithmetic types!");
}
else
{
// code that operates on arg
// if T is not arithmetic
// this part of code is not reached therefore no errors are produced
}
}
Second approach
template <typename...>
struct dependent_false { static constexpr bool value = false; };
template <typename... Ts>
inline constexpr auto dependent_false_v = dependent_false<Ts...>::value;
template <typename T>
void Foo(T arg)
{
if constexpr (std::is_arithmetic_v<std::remove_reference_t<T>>)
{
// code that operates on arg
}
else
{
// dependent_false_v yields false only if previous if branches yield false
static_assert(dependent_false_v<T>, "Function Foo takes only arithmetic types!");
}
}
Third approach
template <typename T>
void Foo(T arg) requires std::is_arithmetic_v<std::remove_reference_t<T>>
{
// code that operates on arg
}
unsigned Foo(unsigned x)
{
return x;
}
struct Bar { operator unsigned () { return 0u; } };
int main()
{
auto x1 = Foo(42u); // x1 == 42, ok
auto x2 = Foo(1.5f); // x2 == 1, unintentional behaviour - precision lost
auto x3 = Foo(-13); // x3 == -13, unnecessary casts
auto x4 = Foo(Bar{}); // x4 == 0, what is Bar?
}
unsigned Foo(unsigned x)
{
return x;
}
template <typename T>
void Foo(T) = delete;
struct Bar { operator unsigned () { return 0u; } };
int main()
{
auto x1 = Foo(42u); // still ok
auto x2 = Foo(1.5f); // compile time error
auto x3 = Foo(-13); // compile time error
auto x4 = Foo(Bar{}); // compile time error
}