De acordo com a especificação OpenMP (2.1. Formato de Diretiva)
Diretivas não podem aparecer em funções constexpr ou em expressões constantes. Pacotes de parâmetros variádicos não podem ser expandidos para uma diretiva ou suas cláusulas, exceto como parte de um argumento de expressão a ser avaliado pela linguagem base, como em uma chamada de função dentro de uma cláusula if.
Eu uso bastante o OpenMP em funções constexpr até que o g++14.2 sem compilador emitiu um aviso. Mas usar o g++15 é um erro.
O seguinte não é um erro do g++14.2, mas sim um erro do g++15.
erro: diretivas OpenMP podem não aparecer em funções 'constexpr' [-Wtemplate-body]
constexpr void func(size_t sz) noexcept
{
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < sz; ++i) something(i);
}
Qual é o motivo dessa restrição? O compilador tem a capacidade de executar código compatível com OpenMP sem habilitar o OpenMP. Esqueci de alguma coisa?
Estou tentando corrigir meu código com a seguinte solução alternativa. O g++15 não proíbe isso, mas não sei se ele quebra novamente o padrão OpenMP e, por exemplo, no g++16, preciso corrigir meu código novamente.
O seguinte é aceito pela especificação OpenMP? É aceito pelo g++15 (e anteriores).
constexpr void func(size_t sz) noexcept
{
auto fcommon = [](size_t i) {};
auto fomp = [sz]()
{
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (size_t i = 0; i < sz; ++i) fcommon(i);
};
auto fseq = [sz]() { for (size_t i = 0; i < sz; ++i) fcommon(i); };
if (std::is_constant_evaluated()) fseq(); else fomp();
}
Geralmente, uma implementação pode oferecer mais do que as especificações garantem. O problema é quando ela oferece menos. O fato de não haver erro antes não deveria ser uma surpresa. Você quebrou as regras, então as especificações não podem ajudar a dizer se o código está correto ou não.
Por outro lado, como você usou e funcionou como esperado, suponho que sua nova variante também funcionaria como esperado. Você simplesmente não pode contar com isso. Pode quebrar na próxima versão do OpenMP ou do compilador.
No seu novo código, você já está ramificando para chamar duas funções diferentes. Você pode ir um passo além escrevendo uma função com as diretivas OpenMp que não são OpenMp
constexpr
e outra funçãoconstexpr
sem OpenMp. Na função real, você ramifica dependendo destd::is_constant_evaluated()
qual chamar qualquer uma das duas. Assim, você estará em conformidade com as especificações OpenMp.