I have some generic code that would like to know when it has been passed a sequence of objects the number of which is known at compile time, as then it can choose an alternative algorithmic strategy. To that end I've tried writing a has_constexpr_size(T)
constexpr function as below which attempts to probe T's size()
member function to see if it can be executed as constexpr.
Note there is a key difference here from the usual "Can I detect a constexpr execution context?" questions because some STL containers like array<T>
always provide a constexpr-available size()
function whereas other STL containers like initializer_list<T>
gain a constexpr-available size()
function if and only if the initializer list is itself constexpr (because the implementation depends on the internal pointer pair, and those need to be constexpr for the size()
to have all-constexpr inputs). The has_constexpr_size(T)
function therefore takes an instance of the type T
being tested in order that it can detect both situations.
Here is a testcase:
#include <array>
#include <type_traits>
#include <utility>
#include <vector>
namespace type_traits
{
namespace detail
{
template <size_t N> struct Char
{
char foo[N];
};
template <class T> constexpr inline Char<2> constexpr_size(T &&v) { return (v.size(), true) ? Char<2>() : throw 0; }
template <class T> inline Char<1> constexpr_size(...) { return Char<1>(); }
}
//! Returns true if the instance of v has a constexpr size()
template <class T> constexpr inline bool has_constexpr_size(T &&v)
{
return noexcept(detail::constexpr_size<T>(std::forward<T>(v)));
}
// Non-constexpr array (always has a constexpr size())
auto ca=std::array<int, 2>();
// Constexpr initializer_list (has constexpr size()). Note fails to compile on VS2015 as its initializer_list isn't constexpr capable yet
constexpr std::initializer_list<int> cil{1, 2};
// Non-constexpr initializer_list (does not have constexpr size())
std::initializer_list<int> il{1, 2};
// Non-constexpr vector (never has constexpr size())
std::vector<int> vec{1, 2};
// Passes on GCC 4.9 and clang 3.8
static_assert(ca.size(), "non-constexpr array size constexpr");
// Passes on GCC 4.9 and clang 3.8
static_assert(cil.size(), "constexpr il size constexpr");
// Fails as you'd expect everywhere with non-constexpr il error
//static_assert(il.size(), "non-constexpr il size constexpr");
// Passes on GCC 4.9, fails on VS2015 and clang 3.8
static_assert(has_constexpr_size(ca), "ca"); // Should NOT fail on VS2015 and clang 3.8
// Fails on GCC 4.9 and clang 3.8. VS2015 doesn't apply.
static_assert(has_constexpr_size(cil), "cil"); // FAILS, and it should not!
// Passes, correct
static_assert(!has_constexpr_size(il), "il");
// Passes, correct
static_assert(!has_constexpr_size(vec), "vec");
constexpr bool test()
{
return has_constexpr_size(std::initializer_list<int>{1, 2});
}
constexpr bool testval=test();
// Fails on GCC 4.9 and clang 3.8. VS2015 doesn't apply.
static_assert(testval, "test()");
}
You'll note the direct static assertion of array<T>::size()
and constexpr initializer_list<T>::size()
works fine on all compilers, as you'd expect. My has_constexpr_size(T)
function works fine for array<T>
, but only on GCC 4.9 which I assume is because of GCC's particularly tolerant constexpr implementation. My has_constexpr_size(T)
function fails for constexpr initializer_list<T>
on all compilers.
So my questions are:
Can a
has_constexpr_size(T)
function be written which correctly detects a constexpr-availablesize()
member function forarray<T>
and constexprinitializer_list<T>
and any other type providing a constexpr-availablesize()
function?If (1) is not possible currently in C++ or in current compilers, can a
has_constexpr_size(T)
function be written which correctly detects a constexpr-availablesize()
member function forarray<T>
and any other type providing an always constexpr-availablesize()
function? Note this solution is not checking for whether simply somesize()
function exists, rather if it is constexpr-available. Sohas_constexpr_size(std::vector<int>())
would be false.