Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE constexpr with std::get

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?

like image 679
helloworld922 Avatar asked Aug 26 '16 10:08

helloworld922


People also ask

Can std :: function be constexpr?

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.

What is if constexpr in C++?

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


1 Answers

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

like image 170
Sam Varshavchik Avatar answered Oct 22 '22 16:10

Sam Varshavchik