Is constexpr useful for overload

2019-02-25 23:37发布

问题:

Is there a way in c++ to get a different overload called based on the runtime/compile time constness of an input? My version(12) of MSVC can't do this using constexpr. Reading c++ documentation, I am not sure if this is the way constexpr works.

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
}

So if this can be done, how? I think there might be a way to use template deduction to do it, but I can't figure out how.

EDIT

I came up with this, but am not sure why it works, && is still fuzy for me, and not sure if this works for everything.

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;
}

If you compile without commenting out the output, it produces this output.

run time
compile time

and it produces this assembly, which is what I want:

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

Are there cases of integer T where this won't work?

回答1:

constexpr can only be applied to variables and functions, but not function parameters (details on cppreference). Furthermore, you cannot overload the function on whether it is computed at compile or run-time, i.e. this is not valid:

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);
}

One way is to give the functions different names and call them accordingly.

Just as a side note,

A constexpr specifier used in a function declaration implies inline.

So you don't need to declare your constexpr function inline

Also, don't forget that constexpr functions are only guaranteed to be evaluated at compile-time if they are used in a context required at compile time. So to force it you would need to write:

 constexpr int a = Flip4('abcd');


回答2:

Your code is illegal.

[C++11, dcl.constexpr] The constexpr specifier shall be applied only to the definition of a variable, the declaration of a function or function template, or the declaration of a static data member of a literal type (3.9). ... [ Note: Function parameters cannot be declared constexpr. — end note ]

constexpr doesn't even exist in MSVC 2013, so you couldn't try it even if you wanted to. Also, if you're wondering why the feature isn't allowed, see constexpr overloading.



回答3:

To elaborate on my comment, you can try this to work around the limitation you're facing:

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;
}

EDIT: Don't lose hope! User-defined literals are here to the rescue :)

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;
}


标签: c++ constexpr