C++ “如何拥有”;constexpr和runtime“;别名

C++ “如何拥有”;constexpr和runtime“;别名,c++,optimization,c++11,compiler-optimization,C++,Optimization,C++11,Compiler Optimization,Constexpr对于编译优化非常有用。例如 strlen(char*) 可以使用…进行预编译 constexpr inline size_t strlen_constexpr(char* baseChar) { return ( ( baseChar[0] == 0 ) ?(// if { 0 )// } :(// else {

Constexpr对于编译优化非常有用。例如

strlen(char*)
可以使用…进行预编译

constexpr inline size_t strlen_constexpr(char* baseChar) {
    return (
            ( baseChar[0] == 0 )
            ?(// if {
              0
              )// }
            :(// else {
              strlen_constexpr( baseChar+1 ) + 1 
              )// }
            );
}
优化后的运行时成本为“0”。。。但运行时的速度要慢10+x以上

// Test results ran on a 2010 macbook air
--------- strlen ---------
Time took for 100,000 runs:1054us.
Avg Time took for 1 run: 0.01054us.
--------- strlen_constexpr ---------
Time took for 100,000 runs:19098us.
Avg Time took for 1 run: 0.19098us.
是否存在任何现有的宏/模板攻击,可以使用单个统一函数来代替。即

constexpr size_t strlen_smart(char* baseChar) {
    #if constexpr
    ... constexpr function
    #else its runtime
    ... runtime function
}
或者一些重载黑客,允许以下

constexpr size_t strlen_smart(char* baseChar) {
    ... constexpr function
}

inline size_t strlen_smart(char* baseChar) {
    ... runtime function
}
注意:该问题适用于一般概念。为runtime和constexpr提供两个单独的函数,而不是给定的示例函数


免责声明:将编译器设置为-O3(优化级别)足以修复99.9%的静态字符优化,使上述所有示例“毫无意义”。但这与这个问题无关,因为它适用于其他“示例”,不仅仅是strlen,我不知道任何通用方法,但我知道两种可能的具体情况

某些编译器的特定情况 另外,gcc和复制gcc所有功能的clang都有一个内置函数
\uuuu内置常量\up
。我不确定gcc是否正确地将内联函数的参数视为常量,但我担心您必须从宏使用它:

#define strlen_smart(s) \
    (__builtin_constant_p(s) && __builtin_constant_p(*s) ? \
        strlen_constexpr(s) : \
        strlen(s))
可能有用。注意,我正在测试constexpr的
s
*s
,因为指向静态缓冲区的指针是编译时常量,而它的长度不是

额外好处:文字的特殊情况(不是实际答案) 对于
strlen
的特定类型转换,可以使用以下事实:字符串文字不是
const char*
类型,而是隐式转换为
const char*
类型的
const char[N]
。但它也会转换为
const char(&)[N]
,而
const char*
则不会

因此,您可以定义:

template <size_t N>
constexpr size_t strlen_smart(const char (&array)[N])
这就有了一个问题

char buffer[10] = { 0 };

strlen_smart(buffer);

应该是0,但优化后的变量只是9。在这样的缓冲区上调用函数没有意义,所以我不在乎。

请记住,即使函数是
constexpr
inline
,传递的参数也是常量,函数结果也是常量,如果在不需要常量表达式的上下文中使用,该函数仍可能在运行时执行。(从技术上讲,它也可能在运行时需要常量表达式的上下文中执行,但这不是有用的编译器所能做的。)您不希望在运行时调用慢版本,即使参数是常量。@hvd i从示例中删除了“const”以避免混淆。同样是的,它仍将由“编译器”选择优化(如果可能)。然而,关键是要确保最有效的变体在运行时运行。我理解,但这不是我的观点。我的意思仅仅是警告你,如果你尝试任何相当于
的东西,它是常数(expr)?StruLun-CONST(ExPR):StruLunSun(EXPR)< /COD>,这将不会阻止<代码> StruLun-Cistt(ExPR)< /C>在运行时被调用。<代码> BaseCar + SIEZOF(CHAR)< /COD> >不,只是C++标准演进工作组讨论的类似问题,并提出了几种解决方法(通过语言扩展或更改)。然而,据我所知,在C++1y中放宽
constexpr
函数应该已经可以显著减少问题了。哇,这是一个巧妙的技巧。不是在寻找一个“cstring”特定的黑客。(因为问题更倾向于一般概念)。但这是我学到的新东西=)至于编译器特定的情况,我们只缺少windows的1。在GCC中,您的
strlen\u smart
在未优化时总是调用
strlen
,但是编译器知道如何优化
strlen
,因此它仍然可以用常量替换它。但是,在进行优化时,我看到对strlen_constexpr的调用在-O处。在-O2或-O3处,这些调用被内联,但没有被常量替换。Testcase:
intmain(){返回strlen_smart(“你好,世界!”)}
。这正是我在对这个问题的评论中所警告的。顺便说一句,对于叮当声,我在优化时确实看到了一个常量,但在不优化时,我看到了对strlen_constexpr的调用。@hvd我实际上为-O3优化添加了一个免责声明。因为我觉得这超出了问题的范围。或者,是否有一个更好、更简单的例子是O3无法优化的?@hvd Yup,这正是vanilla constexpr的问题所在。因此,要解决这个问题,以确保正确的切换发生。(或者至少不是效率低下的constexpr)。正如DyP所评论的,下一个标准正在讨论这一点。正如这个答案所说(第一部分),一个非理想的解决方案。
char buffer[10] = { 0 };

strlen_smart(buffer);