vendredi 3 août 2018

Why does my variadic template argument verifier refuse to evaluate at compile time?

I've got a recursive variadic template function that I can use to evaluate its arguments at compile time, to make sure none of them are larger than a specified maximum value:

 #include <type_traits>

 // base-case
 template <int MaxVal, typename T>
 constexpr T returnZeroIffValuesAreNotTooBig(const T t) 
 {
    return (t>MaxVal)?1:0;
 }

 // recursive-case
 template <int MaxVal, typename T, typename... Rest> constexpr T returnZeroIffValuesAreNotTooBig(const T t, Rest&&... rest)
 {
    return returnZeroIffValuesAreNotTooBig<MaxVal>(t) 
         + returnZeroIffValuesAreNotTooBig<MaxVal>(std::forward<Rest>(rest)...);
 }

 int main(int argc, char ** argv)
 {
    static_assert(returnZeroIffValuesAreNotTooBig<6>(1,2,3)==0, "compiles (as expected)");
    static_assert(returnZeroIffValuesAreNotTooBig<6>(1,2,3,4,5,6,7)==0, "generates compile-time error (as expected, because one of the args is greater than 6)");
    return 0;
 }

... so far, so good, all of the above works fine for me.

Now I want to use that function in my class's variadic constructor:

 template<int MaxVal> class MyClass
 {
 public:
    template<typename ...Vals> constexpr explicit MyClass(Vals&&... vals)
    {
       // verify at compile-time that all values in (vals) are <= MaxVal
       static_assert(returnZeroIffValuesAreNotTooBig<MaxVal>(std::forward<Vals>(vals)...)==0, "why doesn't this compile?");
    }
 };

 int main(int argc, char ** argv)
 {
    MyClass<5> b(1,2,3,4);  // should compile, but doesn't!?
    return 0;
 }

... this won't compile; instead I always get this error:

 $ g++ -std=c++11 ./test.cpp
 ./test.cpp:12:80: error: static_assert expression is not an integral constant
       expression
   ...returnZeroIffValuesAreNotTooBig<MaxVal>(std::forward<Vals>(vals)...)==0...
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
 ./test.cpp:21:15: note: in instantiation of function template specialization
       'MyClass<5>::MyClass<int, int, int, int>' requested here
    MyClass<5> b(1,2,3,4);
               ^
 1 error generated.

... apparently my perfect forwarding is imperfect, and that is preventing the returnZeroIffValuesAreNotToBig function from being evaluated at compile-time? Can someone give me a hint as to what I need to differently in order to get this to work?

Aucun commentaire:

Enregistrer un commentaire