2

I'm trying to make it simple to check at compile time whether the conversion of one value to a new type preserves the value. There may be something in the STL that can do this for me, but I don't see it, so I wrote one. It works, but I wanted to convert it to a class in order to make the use a little simpler for others. I can't get that to work and I feel like I'm missing something obvious.

This works fine:

    template <typename T, typename Q>
    constexpr bool checkV(const Q x) 
    {return x == (T)x && ((x < 0) == ((T)x < 0));}

    static_assert(checkV<unsigned int>(-7), "No");

But the call is ungainly, so I wanted something more like

    CheckIt<unsigned int>(-7)

and so I attempted

    template<typename T>
    class CheckIt {public:
      template<typename Q>
      constexpr CheckIt(const Q x) {static_assert(checkV<T>(x), "No");}
};

I've tried various combinations of const and constexpr (and discovered parameters can't be constexprs, which is annoying). Everything I try results in g++ complaining that x is not a constant expression in the constructor.

What am I missing? checkv, and the constructor, are both always called with constants, like -7. checkv() is happy to evaluate at compile time and I can't see where using a constructor function to wrap that adds any additional burden to the compiler, but obviously it does. Note I need this to work in C++11, but I don't see where later versions will help. I have no problems checking at runtime, with assert, but I want the compile time solution for constants. TIA.

7
  • 1
    x has to be a template parameter. Commented Nov 4, 2019 at 13:53
  • 2
    You are missing that function argument is not a constant expression even if the function itself is a constant expression. I guess writing static_assert(checkV<unsigned int>(-7), "No"); would be the only option for c++11 Commented Nov 4, 2019 at 13:54
  • I want the compile time solution for constants - so you want to detect if the parameter is a constant, and then use static_assert? If it should be only constant, use templates. Commented Nov 4, 2019 at 14:09
  • This doesn't address the question, but in the body of checkV all of the parentheses except for those in the two casts are redundant. Commented Nov 4, 2019 at 14:13
  • passing x as a function parameter would never work, as it's a variable, not a constand expression. Commented Nov 4, 2019 at 14:21

1 Answer 1

1

Passing a variable to a function will not work, as it is not a constant expression, thus the static_assert will not accept it.

You can try passing the value as a template parameter.

template <typename T, typename Q>
constexpr bool checkV(const Q x)
{
    return x == static_cast<T>(x) && ((x < 0) == (static_cast<T>(x) < 0));
}

template <typename T, typename Q, Q N>
void CheckIt() {
    static_assert(checkV<T, Q>(N));
}


int main() {
    constexpr auto val = -7;
    CheckIt<unsigned int, decltype(val), val>();
}

But it's not much cleaner.

edit: your can also use the "good" old macro's

#define CheckIt(x,y) static_assert(checkV<x, decltype(y)>(y));

int main() {
    CheckIt(unsigned int, -7);
}
Sign up to request clarification or add additional context in comments.

1 Comment

I think my problem is that you can't use constexpr to declare a function parameter. I'd need that to work and then SFINAE to allow that template variation to harmlessly fail if the compiler can see the value is not a constant. I can see why the committee never added support for that; this is probably the only case it would be useful. Oh well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.