My question is why the following code is valid C++:
#include <iostream>
#include <tuple>
#include <type_traits>
std::tuple<const char *, const char *> tuple("Hello", "world");
std::integral_constant<std::size_t, 0> zero;
std::integral_constant<std::size_t, 1> one;
template<typename T> const char *
lookup(T n)
{
// I would expect to have to write this:
// return std::get<decltype(n)::value>(tuple);
// But actually this works:
return std::get<n>(tuple);
}
int
main()
{
std::cout << lookup(zero) << " " << lookup(one) << std::endl;
}
Of course, I'm happy to be able to program this way. Moreover, I understand that std::integral_constant has a constexpr conversion operator. However, the parameter n to lookup is not constexpr, so I'm confused as to how a non-static method on a non-constexpr object (even if the method itself is constexpr) can possibly return a compile-time constant.
Of course, we happen to know in this case that the body of the conversion operator doesn't look at the runtime value, but nothing in the type signature guarantees that. For example, the following type obviously doesn't work, even though it, too, has a constexpr conversion operator:
struct bad_const {
const std::size_t value;
constexpr bad_const(std::size_t v) : value(v) {}
constexpr operator std::size_t() const noexcept { return value; }
};
bad_const badone(1);
Is there some extra property of methods that they are considered differently if they ignore the implicit this argument?