2017-07-31 36 views
5

Chciałem obliczyć wartość e w czasie kompilacji (nie martw się, nie zadanie domowe), ale coś poszło nie tak.Możliwe szablonu i constexpr-if niekompatybilność

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1> 
constexpr double e_impl() { 
    if constexpr(limit == 0) { 
     return static_cast<double>(result{}.num)/result{}.den; 
    } 
    return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 
} 

Podczas obliczania wartości są prawidłowe, kompilator zgłasza błąd dotyczący przepełnienia w szablonie. Wygląda na to, że zmienna limit jest poza zakresem (poniżej 0), ale nie powinno się tak stać, ponieważ obudowa 0 jest obsługiwana przez instrukcję if constexpr(…).

Pytanie brzmi: czy się mylę i należy się spodziewać takiego zachowania, czy jest to błąd kompilatora? Skompilowany z GCC 7.1.0.

Odpowiedz

6

Aby uniknąć błędu, należy umieścić drugi powrót wprost do innego oddziału:

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1> 
constexpr double e_impl() { 
    if constexpr(limit == 0) { 
     return static_cast<double>(result{}.num)/result{}.den; 
    } 
    else 
    { 
     return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 
    } 
} 

https://godbolt.org/g/PdV7m7

Rational:

Podczas konkretyzacji szablonu funkcji zakrywające lub rodzajowy lambda , jeśli skonwertowany warunek ma wartość true, a instrukcja zawiera podskładkę constexpr else, to podstancja nie jest tworzona.

http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0128r1.html

To nic nie mówi o nieskrępowanego innego bloku lub bloku, który nie ma działać, więc robi się instancja, rzuca błąd. (Uwaga: nie udaje się również na klang)

+2

Dlaczego ta odpowiedź byłaby bardziej przydatna? –

+0

@ CrazyEddie Nouning przysłówki. Jak ekspresyjnie. – Yakk

5

Nie, to nie jest błąd. Problem jest to, że nawet gdy limit jest 0 i zatrzymania rekursji kompilator nadal znaczków z

return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 

jak to jest bezwarunkowe. Musisz umieścić go w innym bloku, aby uzyskać go tylko do kompilacji, gdy limit nie jest 0.

template<size_t limit = 3, class result = std::ratio<0, 1>, size_t factorial = 1, size_t count = 1> 
constexpr double e_impl() { 
    if constexpr(limit == 0) 
     return static_cast<double>(result{}.num)/result{}.den; 
    else 
     return e_impl<limit - 1, std::ratio_add<result, std::ratio<1, factorial>>, factorial * count, count + 1>(); 
}