首页 > 解决方案 > 编写一个只接受字面量 `0` 或字面量 `1` 作为参数的函数

问题描述

有时对于代数类型,有一个构造函数很方便,它接受一个文字值0来表示中性元素,或者1表示乘法单位元素,即使基础类型不是整数。

问题是如何说服编译器只接受01不接受任何其他整数并不明显。

有没有办法在 C++14 或更高版本中做到这一点,例如结合文字、constexpr 或 static_assert?

让我用一个自由函数来说明(尽管这个想法是将技术用于接受单个参数的构造函数。构造函数也不能接受模板参数)。

一个只接受零的函数可以这样写:

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

问题是,这只能在运行时失败。f_zero(2)我甚至可以写f_zero(2.2),程序仍然可以编译。

第二种情况很容易删除,enable_if例如使用

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

这仍然存在我可以传递任何整数的问题(它只在调试模式下失败)。

在 C++ 11 之前的版本中,一个人能够做到这一点,只接受字面上的零。

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

除了非常难看的错误消息外,这实际上允许 99% 存在。因为,基本上(模 Maquevelian 使用),唯一接受的参数是h_zero(0).

此处说明了这种情况https://godbolt.org/z/wSD9ri。我在 Boost.Units 库中看到了这种技术。

1)现在可以使用 C++ 的新特性做得更好吗?

我问的原因是因为1上面的技术完全失败了。

2)是否有可以应用于字面1情况的等效技巧?(理想情况下作为一个单独的功能)。

我可以想象人们可以发明一种非标准的 long long 文字_c来创建std::integral_constant<int, 0>or的实例,std::integral_constant<int, 1>然后使函数采用这些类型。但是,对于这种情况,生成的语法将是最糟糕的0。也许有更简单的东西。

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

编辑:我应该提到,因为f(0)f(1)可能是完全独立的函数,那么理想情况下它们应该调用不同的函数(或重载)。

标签: c++c++11c++17constexprliterals

解决方案


您可以通过将 0 或 1 作为模板参数传递,如下所示:

template <int value, typename = std::enable_if_t<value == 0 | value == 1>>
void f() {
    // Do something with value
}

然后该函数将被称为:f<0>(). 我不相信构造函数可以做同样的事情(因为你不能为构造函数显式设置模板参数),但是你可以将构造函数设为私有并拥有可以给定模板参数的静态包装函数来执行查看:

class A {
private:
    A(int value) { ... }

public:
    template <int value, typename = std::enable_if_t<value == 0 || value == 1>>
    static A make_A() {
        return A(value);
    }
};

类型的对象A将使用A::make_A<0>().


推荐阅读