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?

4
  • 2
    Issue is that arg parameter is not constexpr... Commented Aug 26, 2016 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.) Commented Aug 26, 2016 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. Commented Aug 26, 2016 at 11:56
  • @helloworld922: constexpr int f(const std::tuple<int>& t) {A<std::get<0>(t)> a_val; return 0;} won't work. Commented Aug 26, 2016 at 13:10

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

5 Comments

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).
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.
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.
clang++4.0 refuses to compile your program, though. And gcc reports 1 whether or not arg is constexpr.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.