Miral Miral - 1 month ago 15
C++ Question

Using C++11 type traits to provide alternate inline implementations

Is the following code pattern reasonable when using traits in templated code where both alternative implementations are always compilable?

Reading the code seems clearer than doing other shenanigans to conditionally compile (but then perhaps I'm just not familiar enough with those shenanigans).

template<typename T>
class X
{
void do_something() noexcept(std::is_nothrow_copy_constructible<T>::value)
{
if (std::is_nothrow_copy_constructible<T>::value)
{
// some short code that assumes T's copy constructor won't throw
}
else
{
// some longer code with try/catch blocks and more complexity
}
}

// many other methods
};


(The added complexity is in part to provide the strong exception guarantee.)

I know this code will work, but is it reasonable to expect the compiler to eliminate the constant-false branches and do inlining etc for the noexcept case (where much simpler than the other case)? I'm hoping for something that would be as efficient in the noexcept case as writing the method with only that first block as body (and vice versa, though I'm less worried about the complex case).

If this isn't the right way to do it, can someone please enlighten me to the recommended syntax?

Answer

If you want to remove the if/else, you can sfinae the return type and clean up the noexcept qualifier.
As an example:

template<typename T>
class X {
    template<typename U = T>
    std::enable_if_t<std::is_nothrow_copy_constructible<T>::value>
    do_something()
    noexcept(true)
    {}

    template<typename U = T>
    std::enable_if_t<not std::is_nothrow_copy_constructible<T>::value>
    do_something()
    noexcept(false)
    {}
};

The drawbacks are that you have now two member functions template.
Not sure it fits your requirements.

If you are allowed to use features from C++17, if constexpr is probably the way to go and you don't have to break your method in two member functions anymore.

Another approach could be based on tag-dispatching the noexceptness of your type.
As an example:

template<typename T>
class X {
    void do_something(std::true_type)
    noexcept(true)
    {}

    void do_something(std::false_type)
    noexcept(false)
    {}

    void do_something()
    noexcept(do_something(std::is_nothrow_copy_constructible<T>{}))
    { do_something(std::is_nothrow_copy_constructible<T>{}); }
};

I know, sfinae is not a verb, but to sfinae something sounds just so good.