编写一个只接受文字“0”或文字“1”作为参数的函数

alf*_*lfC 7 c++ literals constexpr c++11 c++17

有时,对于代数类型来说,即使底层类型不是整数,也可以方便地使用一个构造函数0来表示中性元素或1表示乘法单位元素。

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

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

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

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

constexpr void f_zero(int zero){assert(zero==0); ...}
Run Code Online (Sandbox Code Playgroud)

问题是,这只能在运行时失败。我可以写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);}
Run Code Online (Sandbox Code Playgroud)

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

在 C++ pre 11 中,有能力执行此技巧以仅接受文字零。

struct zero_tag_{}; 
using zero_t = zero_tag_***;
constexpr void h_zero(zero_t zero){assert(zero==nullptr);}
Run Code Online (Sandbox Code Playgroud)

这实际上允许 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);
Run Code Online (Sandbox Code Playgroud)

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

小智 5

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

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

然后该函数将被调用,如: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);
    }
};
Run Code Online (Sandbox Code Playgroud)

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


IlC*_*ano 5

在 C++20 中,您可以使用consteval关键字强制编译时评估。这样你就可以创建一个结构体,它有一个consteval构造函数,并将其用作函数的参数。像这样:

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
}
Run Code Online (Sandbox Code Playgroud)

这里还有一个Godbolt 的例子

编辑:
显然clang 10不会给出如此处所示的错误clang (trunk)在 godbolt 上会出现错误。