Although I've used code like this before, and it's clear that the compiler has enough information to work, I don't really understand why this compiles:
template <class T, class I>
auto foo(const T& t, I i) {
return std::get<i>(t);
}
int main()
{
std::cerr << foo(std::make_tuple(3,4), std::integral_constant<std::size_t, 0>{});
return 0;
}
Live example: http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5.
Seems to work with both gcc and clang. The thing is that while integral_constant has a constexpr conversion to the stored integer, constexpr member functions implicitly take the object itself as an argument, and therefore such a function cannot be used in a constexpr context unless the object we're calling the member function itself can be treated as constexpr.
Here, i is an argument passed to foo, and therefore i most certainly cannot be treated as constexpr. Yet, it is. An even simpler example:
template <class I>
void foo(I i) {
constexpr std::size_t j = i;
}
This compiles too, as long as std::integral_constant<std::size_t, 0>{} is passed to foo.
I feel like I'm missing something obvious about the constexpr rules. Is there an exception for stateless types, or something else? (or, maybe, a compiler bug in two major compilers? This code seems to work on clang 5 and gcc 7.2).
Edit: an answer has been posted, but I don't think it's quite sufficient. In particular, given the last definition of foo, why does:
foo(std::integral_constant<std::size_t, 0>{});
Compile, but not:
foo(0);
Both 0 and std::integral_constant<std::size_t, 0>{} are constant expressions.
Edit 2: It seems like it boils down to the fact that calling a constexpr member function even on an object that is not a constant expression, can itself be regarded as a constant expression, as long as this is unused. This is being taken as obvious. I don't consider this obvious:
constexpr int foo(int x, int y) { return x; }
constexpr void bar(int y) { constexpr auto x = foo(0, y); }
This does not compile, because y as passed into foo is not a constant expression. It's being unused does not matter. Therefore, a complete answer needs to show some kind of language from the standard to justify the fact that a constexpr member function can be used as a constant expression, even on a non-constant expression object, as long as this is unused.