Constante integral pasada por valor, tratada como constexpr?

Aunque he usado código como este antes, y está claro que el compilador tiene suficiente información para funcionar, realmente no entiendo por qué esto 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;
}

Ejemplo en vivo:http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5.

Parece funcionar tanto con gcc como con clang. La cosa es que mientrasintegral_constant tiene unconstexpr conversión al entero almacenado,constexpr Las funciones miembro toman implícitamente el objeto en sí mismo como argumento y, por lo tanto, dicha función no puede utilizarse en unconstexpr contexto a menos que el objeto que estamos llamando la función miembro en sí mismo pueda ser tratado comoconstexpr.

Aquí,i es un argumento pasado afoo, y por lo tantoi ciertamente no puede ser tratado comoconstexpr. Sin embargo, lo es. Un ejemplo aún más simple:

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

Esto también se compila, siempre questd::integral_constant<std::size_t, 0>{} se pasa afoo.

Siento que me falta algo obvio sobre elconstexpr reglas. ¿Existe una excepción para los tipos sin estado, o algo más? (o, tal vez, un error de compilación en dos compiladores principales? Este código parece funcionar en clang 5 y gcc 7.2).

Editar: se ha publicado una respuesta, pero no creo que sea suficiente. En particular, dada la última definición defoo, por que:

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

Compilar, pero no:

foo(0);

Tanto 0 comostd::integral_constant<std::size_t, 0>{} son expresiones constantes

Edición 2: Parece que se reduce al hecho de que llamar a unconstexpr La función miembro, incluso en un objeto que no es una expresión constante, puede considerarse como una expresión constante, siempre quethis Está sin usar. Esto se toma como obvio. No considero esto obvio:

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

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

Esto no se compila, porquey como pasó afoo No es una expresión constante. No se usa, no importa. Por lo tanto, una respuesta completa debe mostrar algún tipo de lenguaje del estándar para justificar el hecho de que unconstexpr la función miembro se puede usar como una expresión constante, incluso en un objeto de expresión no constante, siempre quethis Está sin usar.

Respuestas a la pregunta(2)

Su respuesta a la pregunta