I was looking up compile constant detection and discovered this gem:
struct chkconst {
struct Small { char a; };
struct Big : Small { char b; };
struct Temp { Temp(int x) {} };
static Small chk2(void*);// { return Small(); }
static Big chk2(Temp);// { return Big(); }
};
// There are three major variants I found shown here:
#if CHECK==0
# define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
# define is_const_pos(X) is_const_0( int(X)^(int(X)&INT_MAX) )
# define is_const(X) (is_const_pos(X)|is_const_pos(-int(X))|is_const_pos(-(int(X)+1)))
#elif CHECK==1
# define is_const_0(X) (sizeof(chkconst::chk2(0+!!(X))) != sizeof(chkconst::chk2(0+!(X))))
# define is_const(X) is_const_0( int(X) )
#else
// this one works with const arrays and function parameters in g++
# define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
# define is_const(X) is_const_0( int(X)-int(X) )
#endif
This allows the detection if a value is handled as a constant in the compiler upon testing by using the is_const() macro and does this by detecting if a constant can be evaluated to zero or not at compile time. If it is zero, then the compiler will choose the function that takes a pointer over the function that takes an int, and with that, determine the size of the return value is that of the one taking the pointer, which is very cool.
It was suggested by @David Rodríguez - dribeas here, that it could potentially be used in a SFINAE context. I have tried, but it doesn't seem to work.
template <typename X>
constexpr auto fn(const X x)
-> typename std::enable_if<
std::is_integral<X>::value && is_const(x)
, int>::type
{
return 1;
}
int main()
{
fn(1);
}
The error message is different for each compiler that I've tried. clang states:
test-is_const.cpp:148:3: error: no matching function for call to 'fn'
fn(3);
^~~~~~~
test-is_const.cpp:136:3: note: candidate template ignored: disabled by 'enable_if' [with X = int]
std::is_integral<X>::value && is_const(x)
^
1 error generated.
g++ states different results depending on what CHECK is because it also spits out the type information:
Adrian@K9 ~/winhome/Documents/test-is_const
$ g++ -std=c++11 -O3 -DCHECK=0 test-is_const.cpp && ./a.exe
test-is_const.cpp: In function ‘int main()’:
test-is_const.cpp:148:12: error: no matching function for call to ‘fn(int)’
fn(3);
^
test-is_const.cpp:134:16: note: candidate: template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (((sizeof (chkconst::chk2(((int)(x) ^ ((int)(x) & 2147483647)))) < sizeof (chkconst::Big)) | (sizeof (chkconst::chk2(((int)((-(int)(x))) ^ ((int)((-(int)(x))) & 2147483647)))) < sizeof (chkconst::Big))) | (sizeof (chkconst::chk2(((int)((-((int)(x) + 1))) ^ ((int)((-((int)(x) + 1))) & 2147483647)))) < sizeof (chkconst::Big)))), int>::type fn(X)
constexpr auto fn(const X x)
^
test-is_const.cpp:134:16: note: template argument deduction/substitution failed:
test-is_const.cpp: In substitution of ‘template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (((sizeof (chkconst::chk2(((int)(x) ^ ((int)(x) & 2147483647)))) < sizeof (chkconst::Big)) | (sizeof (chkconst::chk2(((int)((-(int)(x))) ^ ((int)((-(int)(x))) & 2147483647)))) < sizeof (chkconst::Big))) | (sizeof (chkconst::chk2(((int)((-((int)(x) + 1))) ^ ((int)((-((int)(x) + 1))) & 2147483647)))) < sizeof (chkconst::Big)))), int>::type fn(X) [with X = int]’:
test-is_const.cpp:148:12: required from here
test-is_const.cpp:134:16: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
Adrian@K9 ~/winhome/Documents/test-is_const
$ g++ -std=c++11 -O3 -DCHECK=1 test-is_const.cpp && ./a.exe
test-is_const.cpp: In function ‘int main()’:
test-is_const.cpp:148:12: error: no matching function for call to ‘fn(int)’
fn(3);
^
test-is_const.cpp:134:16: note: candidate: template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2((0 + (! !(int)(x))))) != sizeof (chkconst::chk2((0 + (!(int)(x))))))), int>::type fn(X)
constexpr auto fn(const X x)
^
test-is_const.cpp:134:16: note: template argument deduction/substitution failed:
test-is_const.cpp: In substitution of ‘template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2((0 + (! !(int)(x))))) != sizeof (chkconst::chk2((0 + (!(int)(x))))))), int>::type fn(X) [with X = int]’:
test-is_const.cpp:148:12: required from here
test-is_const.cpp:134:16: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
Adrian@K9 ~/winhome/Documents/test-is_const
$ g++ -std=c++11 -O3 -DCHECK=2 test-is_const.cpp && ./a.exe
test-is_const.cpp: In function ‘int main()’:
test-is_const.cpp:148:12: error: no matching function for call to ‘fn(int)’
fn(3);
^
test-is_const.cpp:134:16: note: candidate: template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2(((int)(x) - (int)(x)))) < sizeof (chkconst::Big))), int>::type fn(X)
constexpr auto fn(const X x)
^
test-is_const.cpp:134:16: note: template argument deduction/substitution failed:
test-is_const.cpp: In substitution of ‘template<class X> constexpr typename std::enable_if<(std::is_integral<_Tp>::value && (sizeof (chkconst::chk2(((int)(x) - (int)(x)))) < sizeof (chkconst::Big))), int>::type fn(X) [with X = int]’:
test-is_const.cpp:148:12: required from here
test-is_const.cpp:134:16: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
Does anyone know why this is the case? Is it because it is trying to evaluate the value of the expression where it cannot get such information, only the type?