This is a follow-up question to Detecting constexpr with SFINAE.
I want to detect if an element of a tuple (or anything which can be used with std::get
) is constexpr. So I wrote the following helpers similar to what Xeo gave:
template<size_t> struct sfinae_true : std::true_type{};
template<size_t N, class T>
auto check(const T& arg) -> sfinae_true<(std::get<N>(arg),N)>;
template<size_t N, class>
std::false_type check(...);
Now my test driver code:
int main()
{
constexpr std::tuple<size_t, size_t> arg(4,5);
typedef decltype(check<0,decltype(arg)>(arg)) is_cexpr;
std::cout << "is constexpr? " << is_cexpr::value << '\n';
}
However, this always prints out false
for me! To check that for some reason the false overload isn't always being called, I commented out the false overload and get the compiler error:
note: candidate template ignored: substitution failure [with N = 0, T = const std ::tuple]: non-type template argument is not a constant expression
auto check(const T& arg) -> sfinae_true<(std::get(arg),0)>;
However, I do know that I can call std::get<N>(arg)
and get a constexpr value:
template<size_t N>
class A{};
int main()
{
constexpr std::tuple<size_t, size_t> arg(4,5);
A<std::get<0>(arg)> a_val;
}
This compiles just fine.
I tested this with Clang 3.8.0 on Ubuntu 16.04.
edit:
As a further test based on Sam's answer, I tried the form:
template<size_t N, class T>
auto check(const T& arg)
{
return sfinae_true<(std::get<N>(arg)*0)>();
}
This gets rid of the comma operator entirely, which GCC 5.4.0 compiles just fine, but Clang 3.8.0 still complains about. Interestingly, Clang highlights that arg
itself is not constexpr.
Why is this problematic still? What are the rules for constexpr function arguments?
Constexpr constructors are permitted for classes that aren't literal types. For example, the default constructor of std::unique_ptr is constexpr, allowing constant initialization.
In a constexpr if statement, the value of condition must be a contextually converted constant expression of type bool (until C++23)an expression contextually converted to bool, where the conversion is a constant expression (since C++23).
This looks like a compiler issue.
template<size_t N, class T>
auto check(const T& arg) -> sfinae_true<(std::get<N>(arg),N)>;
gcc fails to compile this:
t.C:8:61: error: template argument 1 is invalid auto check(const T& arg) -> sfinae_true<(std::get(arg),N)>;
But after tweaking this slightly, I get the expected results with gcc 6.1.1:
#include <tuple>
#include <type_traits>
#include <iostream>
template<size_t> struct sfinae_true : std::true_type{};
template<size_t N, class T>
auto check(const T& arg)
{
return sfinae_true<(std::get<N>(arg),N)>();
}
template<size_t N, class>
std::false_type check(...);
int main()
{
constexpr std::tuple<size_t, size_t> arg(4,5);
typedef decltype(check<0,decltype(arg)>(arg)) is_cexpr;
std::cout << "is constexpr? " << is_cexpr::value << '\n';
}
This results in:
is constexpr? 1
Note that commas weren't allowed in constant expressions pre-C++11. Might be something left over from that era...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With