Skip to content

Instantly share code, notes, and snippets.

@MetGang
Last active January 13, 2023 15:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MetGang/64436b70063b39005d43929787cb911b to your computer and use it in GitHub Desktop.
Save MetGang/64436b70063b39005d43929787cb911b to your computer and use it in GitHub Desktop.
C++ Tricks

C++ Tricks

Collection of several C++ tricks that may help you with C++ development.

Made by Patrick Stritch.


Making template errors more readable

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
}

Disallowing unwanted function calls

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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment