Constante integral passada por valor, tratada como constexpr?

Embora eu tenha usado código como esse antes e fique claro que o compilador tem informações suficientes para funcionar, eu realmente não entendo por que isso compila:

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;
}

Exemplo ao vivo:http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5.

Parece funcionar com o gcc e o clang. O problema é que enquantointegral_constant tem umconstexpr conversão para o inteiro armazenado,constexpr funções membro implicitamente tomam o próprio objeto como argumento e, portanto, essa função não pode ser usada em umconstexpr contexto, a menos que o objeto que estamos chamando de função membro possa ser tratado comoconstexpr.

Aqui,i é um argumento passado parafooe, portanto,i certamente não pode ser tratado comoconstexpr. No entanto, é. Um exemplo ainda mais simples:

template <class I>
void foo(I i) {
    constexpr std::size_t j = i;
}

Isso compila também, desde questd::integral_constant<std::size_t, 0>{} é passado parafoo.

Sinto que estou perdendo algo óbvio sobre oconstexpr regras. Existe uma exceção para tipos sem estado, ou algo mais? (ou, talvez, um bug do compilador em dois principais compiladores? Esse código parece funcionar no clang 5 e no gcc 7.2).

Editar: uma resposta foi postada, mas não acho que seja suficiente. Em particular, dada a última definição defoo, porque:

foo(std::integral_constant<std::size_t, 0>{});

Compile, mas não:

foo(0);

0 e 0std::integral_constant<std::size_t, 0>{} são expressões constantes.

Edit 2: Parece que tudo se resume ao fato de chamar umaconstexpr função membro, mesmo em um objeto que não é uma expressão constante, ela mesma pode ser considerada uma expressão constante, desde quethis está sem uso. Isso está sendo tomado como óbvio. Eu não considero isso óbvio:

constexpr int foo(int x, int y) { return x; }

constexpr void bar(int y) { constexpr auto x = foo(0, y); }

Isso não compila, porquey como passadofoo não é uma expressão constante. Não está sendo usado, não importa. Portanto, uma resposta completa precisa mostrar algum tipo de linguagem do padrão para justificar o fato de que umconstexpr A função membro pode ser usada como uma expressão constante, mesmo em um objeto de expressão não constante, desde quethis está sem uso.

questionAnswers(2)

yourAnswerToTheQuestion