Search code examples
c++c++11c++17constexprliterals

Write a function that only accepts literal `0` or literal `1` as argument


Sometimes for algebraic types it is convenient to have a constructor that takes a literal value 0 to denote the neutral element, or 1 to denote the multiplicative identity element, even if the underlying type is not an integer.

The problem is that it is not obvious how to convince the compiler only to accept, 0 or 1 without accepting any other integer.

Is there a way to do this in C++14 or beyond, for example combining literals, constexpr or static_assert?

Let me illustrate with a free function (although the idea is to use the technique for a constructor that take a single argument. Contructors cannot take template parameters either).

A function that accepts zero only could be written in this way:

constexpr void f_zero(int zero){assert(zero==0); ...}

The problem is that, this could only fail at runtime. I could write f_zero(2) or even f_zero(2.2) and the program will still compile.

The second case is easy to remove, by using enable_if for example

template<class Int, typename = std::enable_if_t<std::is_same<Int, int>{}> >
constexpr void g_zero(Int zero){assert(zero==0);}

This still has the problem that I can pass any integer (and it only fails in debug mode).

In C++ pre 11 one had the ability to do this trick to only accept a literal zero.

struct zero_tag_{}; 
using zero_t = zero_tag_***;
constexpr void h_zero(zero_t zero){assert(zero==nullptr);}

This actually allowed one to be 99% there, except for very ugly error messages. Because, basically (modulo Maquevelian use), the only argument accepted would be h_zero(0).

This is situation of affairs is illustrated here https://godbolt.org/z/wSD9ri . I saw this technique being used in the Boost.Units library.

1) Can one do better now using new features of C++?

The reason I ask is because with the literal 1 the above technique fails completely.

2) Is there an equivalent trick that can be applied to the literal 1 case? (ideally as a separate function).

I could imagine that one can invent a non-standard long long literal _c that creates an instance of std::integral_constant<int, 0> or std::integral_constant<int, 1> and then make the function take these types. However the resulting syntax will be worst for the 0 case. Perhaps there is something simpler.

f(0_c);
f(1_c);

EDIT: I should have mentioned that since f(0) and f(1) are potentially completely separate functions then ideally they should call different functions (or overloads).


Solution

  • In C++20 you can use the consteval keyword to force compile time evaluation. With that you could create a struct, which has a consteval constructor and use that as an argument to a function. Like this:

    struct S
    {
    private:
        int x;
    public:
        S() = delete;
    
        consteval S(int _x)
            : x(_x)
        {
            if (x != 0 && x != 1)
            {
                // this will trigger a compile error,
                // because the allocation is never deleted
                // static_assert(_x == 0 || _x == 1); didn't work...
                new int{0};
            }
        }
    
        int get_x() const noexcept
        {
            return x;
        }
    };
    
    void func(S s)
    {
        // use s.get_x() to decide control flow
    }
    
    int main()
    {
        func(0);  // this works
        func(1);  // this also works
        func(2);  // this is a compile error
    }
    

    Here's a godbolt example as well.

    Edit:
    Apperently clang 10 does not give an error as seen here, but clang (trunk) on godbolt does.