Viewing file: constexpr-sfinae.C (7.48 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// Test exercising SFINAE depending on the well-definedness of constexpr // functions. // { dg-do compile { target c++14 } }
#define Assert(e) static_assert ((e), #e)
// Exercise SFINAE based on the absence of integer division by zero. namespace DivByZero {
// Define a pair of functions that have undefined and well-defined // behavior, respectively, due to division by zero, depending on // their arguments.
// The following function is undefined when I is zero, well defined // otherwise. constexpr bool div_zero_0 (int i, int j) { return 1 + j / (i == 0); }
// The following function is undefined when I is non-zero, and well // defined otherwise. constexpr bool div_zero_1 (int i, int j) { return 1 + j / (i != 0); }
// Define a pair of overfloads each of which is viable when the constexpr // function it invokes has well-defined semantics and not otherwise. template <int I> constexpr int f (int (*)[div_zero_0 (I, 0)] = 0) { return 0; }
template <int I> constexpr int f (int (*)[div_zero_1 (I, 0)] = 0) { return 1; }
// Verify that the correct overload is selected based on the template // argument and without triggering a compilation error for the undefined // behavior in the non-viable constexpr function above. Assert (f<0>() == 0); Assert (f<1>() == 1);
}
// Exercise SFINAE based on the absence of signed integer overflow // in addition. namespace IntAddOverflow {
constexpr int a [] = { 1234, __INT_MAX__ / 2 };
constexpr int vflow_0 (int i) { return a [!i] * 7; } constexpr int vflow_1 (int i) { return a [i] * 11; }
template <int I> constexpr int f (int (*)[vflow_0 (I)] = 0) { return 1; }
template <int I> constexpr int f (int (*)[vflow_1 (I)] = 0) { return 0; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 0); Assert (n1 == 1);
}
// Exercise SFINAE based on the absence of signed integer overflow // in multiplication. namespace IntMulOverflow {
constexpr long a [] = { 1234, __LONG_MAX__ / 2 };
constexpr long vflow_0 (int i) { return a [!i] * 3; } constexpr long vflow_1 (int i) { return a [i] * 7; }
template <int I> constexpr int f (int (*)[vflow_0 (I)] = 0) { return 1; }
template <int I> constexpr int f (int (*)[vflow_1 (I)] = 0) { return 0; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 0); Assert (n1 == 1);
}
// Exercise SFINAE based on the absence of undefined pointer arithmetic // involving null pointers. Subtracting one null pointer from another // is well-defined, but subtracting a null pointer from a non-null one // is not. namespace NullPointerArithmetic {
constexpr int i = 0; constexpr const int* a[] = { 0, &i };
// Well-defined core constant expressions involving null pointers. constexpr __PTRDIFF_TYPE__ d00 = a [0] - a [0]; constexpr __PTRDIFF_TYPE__ d11 = a [1] - a [1];
// Undefined core constant expressions involving null pointers. // constexpr __PTRDIFF_TYPE__ d01 = a [0] - a [1]; // constexpr __PTRDIFF_TYPE__ d10 = a [1] - a [0];
// Valid when i == j. constexpr bool nullptr_sub_0 (bool i, bool j) { return 1 + a [!i] - a [!j]; }
// Valid when i != j. constexpr bool nullptr_sub_1 (bool i, bool j) { return 1 + a [i] - a [!j]; }
// Selected when I == 0. template <bool I> constexpr int f (int (*)[nullptr_sub_0 (I, 0)] = 0) { return 0; }
// Selected when I != 0. template <bool I> constexpr int f (int (*)[nullptr_sub_1 (I, 0)] = 0) { return 1; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 0); Assert (n1 == 1);
}
// Exercise SFINAE based on the absence of undefined pointer arithmetic // involving null poiinters. Subtracting one null pointer from another // is well-defined, but subtracting a null pointer from a non-null one // is not. namespace NullPointerDereference {
struct S { int a, b; };
constexpr S s = { }; constexpr const S* a[] = { 0, &s };
constexpr bool nullptr_ref_0 (int i) { return &a [i != 0]->b == &s.b; } constexpr bool nullptr_ref_1 (int i) { return &a [i == 0]->b == &s.b; }
template <int I> constexpr int f (int (*)[nullptr_ref_0 (I)] = 0) { return 1; }
template <int I> constexpr int f (int (*)[nullptr_ref_1 (I)] = 0) { return 0; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 0); Assert (n1 == 1);
}
// Exercise SFINAE based on whether or not two constexpr function // calls have a circular depency on one another such that a call // to one would not terminate. namespace CircularDependency {
constexpr bool call_me (int i, bool (*f)(int)) { return f (i); }
constexpr bool undefined_if_0 (int i) { return i ? 1 : call_me (i, undefined_if_0); }
constexpr bool undefined_if_1 (int i) { return i ? call_me (i, undefined_if_1) : 1; }
template <int I> constexpr int f (int (*)[undefined_if_0 (I)] = 0) { return 0; }
template <int I> constexpr int f (int (*)[undefined_if_1 (I)] = 0) { return 1; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 1); Assert (n1 == 0);
}
// Exercise SFINAE based on whether constexpr functions flow off // the end without returning a value. namespace FlowOffTheEnd {
constexpr bool undefined_if_0 (int i) { switch (i) case 1: return 1; } constexpr bool undefined_if_1 (int i) { switch (i) case 0: return 1; }
template <int I> constexpr int f (int (*)[undefined_if_0 (I)] = 0) { return 1; }
template <int I> constexpr int f (int (*)[undefined_if_1 (I)] = 0) { return 0; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 0); Assert (n1 == 1);
}
// Exercise SFINAE based on the presence and absence of a left shift // expression with a negative second operand. namespace NegativeLeftShift {
constexpr int a [] = { -1, 1 };
constexpr int undefined_if_0 (int i) { return 1 << a [i]; } constexpr int undefined_if_1 (int i) { return 1 << a [!i]; }
template <int I> constexpr int f (int (*)[undefined_if_0 (I)] = 0) { return 0; }
template <int I> constexpr int f (int (*)[undefined_if_1 (I)] = 0) { return 1; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 1); Assert (n1 == 0);
}
// Exercise SFINAE based on the presence and absence of a right shift // expression with a negative second operand. namespace NegativeRightShift {
constexpr int a [] = { -1, 1 };
constexpr int undefined_if_0 (int i) { return 2 >> a [i]; } constexpr int undefined_if_1 (int i) { return 2 >> a [!i]; }
template <int I> constexpr int f (int (*)[undefined_if_0 (I)] = 0) { return 0; }
template <int I> constexpr int f (int (*)[undefined_if_1 (I)] = 0) { return 1; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 1); Assert (n1 == 0);
}
// Exercise SFINAE based on the absence of signed integer overflow // in a signed left shift expression. namespace LeftShiftOverflow {
constexpr int a[] = { 1234, 1 };
constexpr int undefined_if_0 (int i) { return 1 << a [i]; } constexpr int undefined_if_1 (int i) { return 1 << a [!i]; }
template <int I> constexpr int f (int (*)[undefined_if_0 (I)] = 0) { return 0; }
template <int I> constexpr int f (int (*)[undefined_if_1 (I)] = 0) { return 1; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 1); Assert (n1 == 0);
}
// Exercise SFINAE based on the absence of using a negative array // index. namespace NegativeArrayIndex {
constexpr int a [] = { -1, 1 };
constexpr int undefined_if_0 (int i) { return 2 + a [a [i]]; } constexpr int undefined_if_1 (int i) { return 2 + a [a [!i]]; }
template <int I> constexpr int f (int (*)[undefined_if_0 (I)] = 0) { return 0; }
template <int I> constexpr int f (int (*)[undefined_if_1 (I)] = 0) { return 1; }
constexpr int n0 = f<0>(); constexpr int n1 = f<1>();
Assert (n0 == 1); Assert (n1 == 0);
}
|