使用C++ 11类型特征来提供备用内联实现

Mir*_*ral 28 c++ templates exception-handling type-traits c++11

在模板化代码中使用特征时,以下代码模式是否合理,其中两个替代实现始终可编译?

阅读代码似乎比做其他恶作剧有条件地编译更清楚(但也许我对那些恶作剧我不够熟悉).

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
};
Run Code Online (Sandbox Code Playgroud)

(增加的复杂性部分是为了提供强有力的异常保证.)

我知道这段代码会起作用,但是期望编译器消除常量假分支并为noexcept情况做内联等(比其他情况简单得多)是否合理?我希望在noexcept情况下能够像只用第一个块作为主体来编写方法一样有效(反之亦然,尽管我不太担心复杂的情况).

如果这不是正确的方法,有人可以请教我推荐的语法吗?

sky*_*ack 21

[...]期望编译器消除常量错误分支并对noexcept情况进行内联等(比其他情况简单得多)是否合理?

它可能是,但我不会依赖它,因为你无法控制它.


如果你想删除的if/else,你可以SFINAE的返回类型和清理noexcept预选赛.
举个例子:

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)
    {}
};
Run Code Online (Sandbox Code Playgroud)

缺点是你现在有两个成员函数模板.
不确定它是否符合您的要求.

如果允许您使用C++ 17中的功能,那么if constexpr可能就是这样,您不必再破坏两个成员函数中的方法.

另一种方法可以基于标记 - 调度noexcept您的类型的ness.
举个例子:

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>{}); }
};
Run Code Online (Sandbox Code Playgroud)

我知道,sfinae不是一个动词,但对于sfinae来说听起来真是太棒了.

  • Sfinae出了问题.最后两行确实让我为'noexcept(auto)`而哭泣.另外,你可以直接在`std :: integral_constant <bool,...>`中包装`std :: is _... <T> :: value`(很快`std :: boolean_constant <...>`) . (2认同)

sba*_*bbi 13

是否合理期望编译器消除常量错误的分支并为noexcept案例进行内联等[...]?

是.话虽这么说,必须实例化常量 - 错误分支,这可能会或可能不会导致编译器实例化您不需要的一堆符号(然后您需要依赖链接器来删除它们).

我仍然会选择SFINAE shenanigans(实际上是tag-dispatching),这在C++ 11中可以很容易地完成.

template<typename T>
class X
{
    void do_something() noexcept(std::is_nothrow_copy_constructible<T>::value)
    {
        do_something_impl(std::is_nothrow_copy_constructible<T>() ); 
    }

    void do_something_impl( std::true_type /* nothrow_copy_constructible */ )
    {
        // some short code that assumes T's copy constructor won't throw
    }

    void do_something_impl( std::false_type /* nothrow_copy_constructible */)
    {
        // some longer code with try/catch blocks and more complexity
    }

    // many other methods
};
Run Code Online (Sandbox Code Playgroud)

如果您要检查nothrow_copy_constructor所有其他方法,可以考虑专门化整个班级:

template<typename T, class = std::is_nothrow_copy_constructible_t<T> >
class X
{
   //throw copy-ctor implementation
};

template<typename T>
class X<T, std::true_type>
{
   // noexcept copy-ctor implementation
};
Run Code Online (Sandbox Code Playgroud)


Leo*_*eon 8

期望编译器消除常量假分支是否合理?

是的,死代码消除是最简单的优化之一.

...并为noexcept案例进行内联等工作?

我的第一个冲动是回答"不,你不能依赖它,因为它取决于内联通道在优化流程中相对于死代码消除步骤的位置".

但是经过更多的反思,我无法理解为什么在内联步骤之前和之后,在足够高的优化级别上的成熟编译器不会执行死代码消除.所以这个期望也应该是合理的.

但是,关于优化的猜测从来都不是肯定的.转到简单的实现并获得正确运行的代码.然后测量其性能并检查您的假设是否正确.如果他们不是 - 重新设计你的情况的实施将不会花费比从一开始就走下保证路径更多的时间.


Yak*_*ont 6

每个成熟的编译器都会消除死代码.每个成熟的编译器都会检测到常量分支,并且死代码会检测到

您可以使用十几个模板参数创建一个函数,该函数if在其正文中使用天真的检查,并且难以查看结果 - 这不会有问题.

如果您执行创建static变量或thread_local实例化符号等操作,则更难以消除这些内容.

内联有点琐碎,因为编译器倾向于在某些时候放弃内联; 代码越复杂,编译器在内联之前放弃的可能性就越大.

在C++ 17中,您可以升级ifconstexpr版本.但是在C++ 14和11中,你的代码会做得很好.它比其他选择更简单,更容易阅读.

它有点脆弱,但如果它打破了它通常在编译时以嘈杂的方式这样做.