Detecting constexpr () member function in C ++ 14

I have a common code that I would like to know when it was given a sequence of objects, the number of which is known at compile time, since then it can choose an alternative algorithmic strategy. To this end, I tried to write the has_constexpr_size(T) constexpr function, as shown below, which tries to define the member function T size() to see if it can be executed as constexpr.

Note that there is a key difference from the usual "Can I define the execution context of constexpr?" because some STL containers, such as array<T> , always provide the size() function contained in constexpr, while other STL containers, such as initializer_list<T> , get a constexpr-accessible size() function if and only if when the initializer list itself is constexpr (since the implementation depends on the internal pair of pointers, and they must be constexpr for size() to enter all-constexpr). Thus, the has_constexpr_size(T) function accepts an instance of the test type T so that it can detect both situations.

Here is the test file:

 #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 will notice that the direct static statement array<T>::size() and constexpr initializer_list<T>::size() works fine on all compilers, as you would expect. My has_constexpr_size(T) function works fine for array<T> , but only for GCC 4.9, which I assume is due to GCC, especially the tolerant implementation of constexpr. My has_constexpr_size(T) function does not work for constexpr initializer_list<T> for all compilers.

So my questions are:

  • Can the has_constexpr_size(T) function be written that correctly detects the constant-accessible member function size() for array<T> and constexpr initializer_list<T> and any other type that provides the size() function constexpr-available?

  • If (1) is currently not possible in C ++ or in current compilers, the has_constexpr_size(T) function can be written that correctly detects the member function size() constexpr for array<T> and any other type that always provides constexpr- available size() function? Note This solution does not check if there is any size() function, not constexpr. Thus, has_constexpr_size(std::vector<int>()) will be false.

+6
source share
1 answer

Edit: Below, unfortunately, it is not true if you replace std::string as a type instead of int , for example. std::array<std::string, 2> you will find that it does not work. For some reason this doesn't make any sense to me, C ++ 14 treats the following as:

  static_assert(static_cast<std::array<int, 2> *>(0)->size(), "foo"); // passes static_assert(static_cast<std::array<std::string, 2> *>(0)->size(), "foo"); // fails 

I'm officially stunned. In any case, I will leave the answer below for historical interest.


I have a working solution for my own question 2, where is the use of Expression SFINAE I can detect a nonzero constant member function size() for array<> and correctly exclude it for vector<> , but it gets constexpr initializer_list<> incorrectly. Confirmed work on GCC 4.9 and clang 3.8 and VS2015:

 #include <array> #include <type_traits> #include <utility> #include <vector> namespace type_traits { namespace detail { template <size_t N> struct Char { char foo[N]; }; // Overload only available if a default constructed T has a constexpr-available non-zero size() template <class T, size_t N = T{}.size() + 1> constexpr inline Char<N> constexpr_size(const T &) { return Char<N>(); } template <class T> constexpr 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(const T &v) { return sizeof(detail::constexpr_size<typename std::decay<T>::type>( std::move(v))) > 1; } // 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 constructible yet #ifndef _MSC_VER constexpr std::initializer_list<int> cil{1, 2}; #endif // 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}; // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(ca.size(), "non-constexpr array size constexpr"); // Correct on GCC 4.9 and clang 3.8. #ifndef _MSC_VER static_assert(cil.size(), "constexpr il size constexpr"); #endif // Fails as you'd expect everywhere with non-constexpr il error // static_assert(il.size(), "non-constexpr il size constexpr"); // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(has_constexpr_size(ca), "ca"); // Incorrect on GCC 4.9 and clang 3.8 and VS2015 #ifndef _MSC_VER static_assert(!has_constexpr_size(cil), "cil"); // INCORRECT! #endif // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(!has_constexpr_size(il), "il"); // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(!has_constexpr_size(vec), "vec"); constexpr bool test_ca() { return has_constexpr_size(std::array<int, 2>{1, 2}); } constexpr bool testca = test_ca(); // Correct on GCC 4.9 and clang 3.8 and VS2015 static_assert(testca, "testca()"); constexpr bool test_cil() { return has_constexpr_size(std::initializer_list<int>{1, 2}); } constexpr bool testcil = test_cil(); // Incorrect on GCC 4.9 and clang 3.8 and VS2015 static_assert(!testcil, "testcil()"); // INCORRECT! } 

So, now we need some method of filtering input types without constexpr, so we can correctly return false for non-constexpr input. I suspect this is the same unresolved issue as elsewhere in stackoverflow :(

Any further promotion or thoughts are greatly appreciated.

0
source

All Articles