【问题标题】:Is constexpr useful for overloadconstexpr 对重载有用吗
【发布时间】:2015-10-17 23:37:52
【问题描述】:

C++ 中有没有办法根据输入的运行时/编译时常量调用不同的重载?我的 MSVC 版本 (12) 无法使用 constexpr 执行此操作。阅读 c++ 文档,我不确定这是否是 constexpr 的工作方式。

inline int Flip4(constexpr int n) {
    return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
inline int Flip4(int n) {
    return _byteswap_ulong(n);
}

int main(int argc, char* argv[]) {
    int a = Flip4('abcd');  // calc at compile time
    int b = Flip4(argc);  // calc at runtime
}

那么,如果可以做到这一点,该怎么做?我认为可能有一种方法可以使用模板推导来做到这一点,但我不知道怎么做。

编辑

我想出了这个,但不确定它为什么会起作用,&& 对我来说仍然很模糊,并且不确定这是否适用于所有事情。

template<class T> typename std::enable_if<std::is_arithmetic<T>::value, int>::type
inline Flip4(T&& n) {
    //cout << "compile time" << endl;
    return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
template<class T> typename std::enable_if<!std::is_arithmetic<T>::value, int>::type
inline Flip4(T&& n) {
    //cout << "run time" << endl;
    return _byteswap_ulong(n);
}

int main(int argc, char* argv[]) {
    int n = Flip4(argc);
    n += Flip4(1);
    return n;
}

如果你编译时没有注释掉输出,它会产生这个输出。

run time
compile time

它会生成这个程序集,这就是我想要的:

int n = Flip4(argc);
000000013FA11270  bswap       ecx  
n += Flip4(1);
000000013FA11272  lea         eax,[rcx+1000000h] 

是否存在整数 T 不起作用的情况?

【问题讨论】:

  • 不,没有constexpr参数。
  • 我该怎么做这样的事情。
  • 如何将第一个“重载”改为template &lt;int n&gt; inline int Flip4() { ... },然后用Flip4&lt;0xc0de&gt;() 调用它?
  • 刚刚检查过 - 当您使用 _byteswap_ulong 时,VS2015 也会在编译期间计算常量输入的值。所以一切都很好——根本不需要constexpr。我之前评论中链接中代码的程序集(但使用_byteswap_ulong而不是班次)是bswap ecxlea eax,[rcx+64636261h]
  • Clang 和最新版本的 GCC 应该在编译时计算这些值,并为 Flip4 仅定义为 constexpr 的非 const 值发出 bswap。根据@Rostislav 的说法,MSVC '15 也可以在编译时使用内在函数评估常量。

标签: c++ constexpr


【解决方案1】:

constexpr只能作用于变量和函数,不能作用于函数参数(详见cppreference)。此外,您不能重载函数是否在编译时或运行时计算,即这是无效的:

constexpr int Flip4(int n) {
    return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}

inline int Flip4(int n) {
    return _byteswap_uint64(n);
}

一种方法是为函数赋予不同的名称并相应地调用它们。

顺便说一句,

函数声明中使用的constexpr 说明符暗示inline

所以你不需要声明你的constexpr函数inline

另外,不要忘记 constexpr 函数只有在编译时需要的上下文中使用时才保证在编译时进行评估。所以要强制它,你需要写:

 constexpr int a = Flip4('abcd');

【讨论】:

  • 您可以使用默认参数bool compile_time = true,然后调用Flip4(stuff, false) 显式调用运行时版本。
  • @melak47 是的 - 有很多不同的方法可以实现这一点。更改了措辞 :) 谢谢!
  • 感谢隐含的内联注释。我不知道!
  • 我有理由需要重载解决方案。 1)怎么说呢……不成熟的开发者,2)遗留代码。
  • @johnnycrash 是的,从你的其他 cmets 那里得到的。请参阅我对原始帖子的最后评论-也许会有所帮助。
【解决方案2】:

你的代码是非法的。

[C++11, dcl.constexpr] constexpr 说明符仅适用 变量的定义、函数的声明或 函数模板,或声明一个静态数据成员 文字类型(3.9)。 ... [ 注意: 函数参数不能 声明为 constexpr。 — 尾注 ]

constexpr 在 MSVC 2013 中甚至都不存在,因此即使您想尝试也无法尝试。此外,如果您想知道为什么不允许使用该功能,请参阅 constexpr overloading

【讨论】:

  • 请注意,在链接的答案中,我提供了一个反对 constexpr-overloading 的特定预期用途(字符串池)的论据,而不是反对一般假设的特征。
  • 这对我来说是个问题,因为我只能加快运行时常量或非运行时常量的情况,但不能同时加快速度 - 除非我创建 2 个不同的函数。
【解决方案3】:

要详细说明我的评论,您可以尝试解决您面临的限制:

Run It Online

#include <iostream>
using std::cout;
using std::endl;

template <int n>
constexpr int Flip4() {
    return ((n & 0xFF) << 24) | ((n & 0xFF00) << 8) | ((n & 0xFF0000) >> 8) | ((n & 0xFF000000) >> 24);
}
inline int Flip4(int n) {
    return _byteswap_ulong(n);
}

int main() {
    constexpr int a = Flip4<0xabcd>();  // calc at compile time
    int b = Flip4(0xabcd);  // calc at runtime

    static_assert(a == -844431360, "");

    cout << "a: " << a << endl;
    cout << "b: " << b << endl;
}

编辑:不要失去希望! User-defined literals 来救援 :)

Run It Online

#include <iostream>
using std::cout;
using std::endl;

// wraps a single integer (unsigned long long) in order to use it in a user-defined literal
// the type (unsigned long long) is a limitation of the standard: https://stackoverflow.com/a/16596909/865719
struct IntegerWrapper
{
    const unsigned long long value;
    constexpr explicit IntegerWrapper(unsigned long long val) : value{val} {}
};
// user-defined literal
constexpr IntegerWrapper operator "" _iw (const unsigned long long value) 
{ 
    return IntegerWrapper{value};
}

constexpr int Flip4(IntegerWrapper&& n) {
    return ((n.value & 0xFF) << 24) | ((n.value & 0xFF00) << 8) | ((n.value & 0xFF0000) >> 8) | ((n.value & 0xFF000000) >> 24);
}

inline int Flip4(int n) {
    return _byteswap_ulong(n);
}

int main() {

    constexpr int a = Flip4(0xabcd_iw);  // calc at compile time
    const     int b = Flip4(0xabcd);     // calc at runtime

    static_assert(a == -844431360, "");

    cout << "a: " << a << endl;
    cout << "b: " << b << endl;
}

【讨论】:

  • 这很好,但我想使用某种重载解决方案,所以我没有 2 个函数。
  • 这很酷。但是你仍然必须做一些不同的事情来调用这两个版本。查看我对我的编辑
猜你喜欢
  • 2012-02-14
  • 2017-06-27
  • 2013-06-26
  • 2020-02-28
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 2023-03-03
相关资源
最近更新 更多