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 parafoo
e, 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.