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&nbsp;tem umconstexpr&nbsp;conversão para o inteiro armazenado,constexpr&nbsp;funções membro implicitamente tomam o próprio objeto como argumento e, portanto, essa função não pode ser usada em umconstexpr&nbsp;contexto, a menos que o objeto que estamos chamando de função membro possa ser tratado comoconstexpr.

Aqui,i&nbsp;é um argumento passado parafooe, portanto,i&nbsp;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>{}&nbsp;é passado parafoo.

Sinto que estou perdendo algo óbvio sobre oconstexpr&nbsp;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>{}&nbsp;são expressões constantes.

Edit 2: Parece que tudo se resume ao fato de chamar umaconstexpr&nbsp;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&nbsp;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&nbsp;como passadofoo&nbsp;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&nbsp;A função membro pode ser usada como uma expressão constante, mesmo em um objeto de expressão não constante, desde quethis&nbsp;está sem uso.