8

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.

  • Why does the check function not detect the constexpr-ness correctly?
  • How do I fix this?

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?

Community
  • 1
  • 1
helloworld922
  • 10,801
  • 5
  • 48
  • 85
  • 2
    Issue is that `arg` parameter is not `constexpr`... – Jarod42 Aug 26 '16 at 11:34
  • 1
    (To expand slightly on what I think @Jarod42 means: `arg` being a function parameter is never considered as a constant expression, even if the argument you provide to the function is a constant expression.) – dyp Aug 26 '16 at 11:43
  • How would I detect if the result of a function call would be constexpr if this method doesn't work? Clearly I can use the tuple as a constexpr value by constructing the `a_val` object. – helloworld922 Aug 26 '16 at 11:56
  • @helloworld922: `constexpr int f(const std::tuple& t) {A(t)> a_val; return 0;}` won't work. – Jarod42 Aug 26 '16 at 13:10

1 Answers1

3

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...

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • I tried your tweak in clang 3.8.0 and it doesn't work, but it does with GCC 5.4.0. Interestingly, the part which clang highlights as being non-constexpr is not the comma operator, but the fact that `arg` is non-constexpr (I tested this by removing the comma operator). – helloworld922 Aug 26 '16 at 11:10
  • The mandated behaviour is different in your version. SFINAE does not apply to deduced return types; SFINAE is in the immediate context only whereas return type deduction requires a rather deep instantiation of the function template. – dyp Aug 26 '16 at 11:39
  • Interestingly enough, @dyp, the end result is the intended result. A non-constexpr parameter did not result in a compilation error, but the expected default template. It looks to me like the template function's return type is deduced at declaration time, and then participates in SFINAE resolution normally. – Sam Varshavchik Aug 26 '16 at 11:44
  • 2
    clang++4.0 refuses to compile your program, though. And gcc reports 1 whether or not `arg` is `constexpr`. – dyp Aug 26 '16 at 11:47
  • Please see http://eel.is/c++draft/dcl.spec.auto#11 and http://stackoverflow.com/q/24396819 – dyp Aug 26 '16 at 11:50