5

Let's say this the bare bone for a non zero-able type.

template<typename T>
struct NonZero {
    T val;
};

My question is if it's possible to make a constructor for NonZero which to take a literal of type T and statically check if it's non-zero and then assigns it to val.

NonZero<int> n( 0 ); // compilation error
NonZero<int> n( 1 ); // ok

Or is there a better way to archieve a non zero type?

Codor
  • 17,447
  • 9
  • 29
  • 56
Sherushe
  • 78
  • 1
  • 5
  • To my understanding, the specific implementations can be provided only by template specialization; is that what is desired? – Codor Apr 04 '17 at 14:01
  • I was wondering if there was an easier way, because `NonZero n( var )` should also be possible. – Sherushe Apr 04 '17 at 14:03

3 Answers3

5

Since the value is known at the compile time, you could make it a template argument and use std::enable_if:

template<typename T, T x>
struct NonZero {
    const static std::enable_if_t<x != T(0), T> value = x;
};

Usage:

int x = NonZero<int, 1>::value; // OK
int x2 = NonZero<int, 0>::value;// Compilation error
alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
3

Another option would be to check a value for being zero in constexpr constructor:

template<typename T>
struct NonZero {
    const T value;
    constexpr NonZero(const T val) :
        value(val != 0 ? val : throw std::runtime_error("should be non-zero"))
    {}
};

Usage:

NonZero<int> v(1);            // OK
NonZero<int> v2(0);           // Compiles OK, but throws in run-time
constexpr NonZero<int> v3(1); // OK
constexpr NonZero<int> v4(0); // Compilation error

This method is only applicable to constexpr variables, but looks much more straightforward.

Also, instead of using a struct with constexpr constructor, you could use a constexpr function or a user-defined literal, with exactly the same idea.

alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
  • I'm just getting an error in gcc saying `expression '' is not a constant-expression`. Do you have an idea of the problem? – Sherushe Apr 04 '17 at 14:27
  • 2
    I'm so stupid, nevermind my comment, of course there is a compilation error when the variable is constructed with a 0. Thank you anyway, it was exactly what I was looking for. – Sherushe Apr 04 '17 at 14:39
  • 1
    @Sherushe You're welcome. It was a wonderful question to answer. – alexeykuzmin0 Apr 04 '17 at 14:47
1

Just thought of another approach, which will provide you a constructor which takes a value as an argument.

The core idea is to make the constructor templated and pass the value as a template argument. Unfortunately, it's impossible to do this in a straightforward way, but we can use a workaround from this question:

template<typename T>
struct NonZero {
    const T value;

    template<T x>
    NonZero(std::integral_constant<std::enable_if_t<x != T(0), T>, x>) : value(x) {
    }
};

Usage:

auto v  = NonZero<int>(std::integral_constant<int, 1>()); // OK
auto v2 = NonZero<int>(std::integral_constant<int, 0>()); // Compilation error
Community
  • 1
  • 1
alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51