alf*_*lfC 7 c++ literals constexpr c++11 c++17
有时,对于代数类型来说,即使底层类型不是整数,也可以方便地使用一个构造函数0来表示中性元素或1表示乘法单位元素。
问题是如何说服编译器只接受0或1不接受任何其他整数并不明显。
有没有办法在 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>()。
在 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 上会出现错误。
| 归档时间: |
|
| 查看次数: |
352 次 |
| 最近记录: |