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.
有没有办法在C ++ 14或更高版本中做到这一点,例如结合文字,constexpr或static_assert?
让我用一个自由函数来说明(尽管这个想法是对带有单个参数的构造函数使用该技术)。
仅接受零的函数可以这样编写:
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);}
这仍然存在我可以传递任何整数的问题。
在C ++ pre 11中,一个人有能力做到这一点,只接受文字零。
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)现在可以使用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?
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);