调用 static_assert(false) 的正确方法是什么?

发布于 2025-01-17 07:37:15 字数 1721 浏览 2 评论 0 原文

我正在尝试使用 static_assert 来强制某些事情失败。如果您尝试以特定方式实例化特定模板化函数,我想生成编译器错误。我可以让它工作,但它真的很难看。有没有更简单的方法来做到这一点?

这是我的第一次尝试。这根本不起作用。即使没有人尝试使用此功能,它总是会生成错误。

template< class T >
void marshal(std::string name, T  *value)
{
  static_assert(false, "You cannot marshal a pointer.");
}

这是我的第二次尝试。它确实有效。如果您不调用此方法,则不会出现错误。如果您确实调用此函数,您将收到一条非常易读的错误消息,该消息指向该行并指向尝试实例化它的代码。

template< class T >
void marshal(std::string name, T  *value)
{
  static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "You cannot marshal a pointer.");
}

问题是这段代码充其量是丑陋的。它看起来像一个黑客。我担心下次我更改优化级别、升级编译器、打喷嚏等时,编译器会意识到第二种情况与第一种情况相同,并且它们都会停止工作。

有更好的方法来做我想做的事情吗?

这是一些背景信息。我想要有几个不同版本的 marshal() ,它们适用于不同的输入类型。我想要一个使用模板作为默认情况的版本。我想要另一个明确禁止除 char * 之外的任何指针的指针。

void marshal(std::string name, std::string)
{ 
  std::cout<<name<<" is a std::string type."<<std::endl;
}

void marshal(std::string name, char *string)
{
  marshal(name, std::string(string));
}

void marshal(std::string name, char const *string)
{
  marshal(name, std::string(string));
}

template< class T >
void marshal(std::string name, T value)
{
  typedef typename std::enable_if<std::is_pod<T>::value>::type OnlyAllowPOD;
  std::cout<<name<<" is a POD type."<<std::endl;
}

template< class T >
void marshal(std::string name, T  *value)
{
  static_assert(false, "You cannot marshal a pointer.");
}

int main (int argc, char **argv)
{
  marshal("should be pod", argc);
  marshal("should fail to compile", argv);
  marshal("should fail to compile", &argc);
  marshal("should be std::string", argv[0]);
}

I’m trying to use static_assert to force something to fail. If you try to instantiate a specific templated function in a specific way I want to generate a complier error. I could make it work, but it was really ugly. Is there an easier way to do this?

This was my first attempt. This did not work at all. It always generates an error, even if no one tries to use this function.

template< class T >
void marshal(std::string name, T  *value)
{
  static_assert(false, "You cannot marshal a pointer.");
}

Here’s my second attempt. It actually works. If you don’t call this, you get no error. If you do call this, you get a very readable error message that points to this line and points to the code that tried to instantiate it.

template< class T >
void marshal(std::string name, T  *value)
{
  static_assert(std::is_pod<T>::value && !std::is_pod<T>::value, "You cannot marshal a pointer.");
}

The problem is that this code is ugly at best. It looks like a hack. I’m afraid the next time I change the optimization level, upgrade my compiler, sneeze, etc, the compiler will realize that this second case is the same as the first, and they will both stop working.

Is there a better way to do what I’m trying to do?

Here’s some context. I want to have several different versions of marshal() which work for different input types. I want one version that uses a template as the default case. I want another one that specifically disallows any pointers except char *.

void marshal(std::string name, std::string)
{ 
  std::cout<<name<<" is a std::string type."<<std::endl;
}

void marshal(std::string name, char *string)
{
  marshal(name, std::string(string));
}

void marshal(std::string name, char const *string)
{
  marshal(name, std::string(string));
}

template< class T >
void marshal(std::string name, T value)
{
  typedef typename std::enable_if<std::is_pod<T>::value>::type OnlyAllowPOD;
  std::cout<<name<<" is a POD type."<<std::endl;
}

template< class T >
void marshal(std::string name, T  *value)
{
  static_assert(false, "You cannot marshal a pointer.");
}

int main (int argc, char **argv)
{
  marshal("should be pod", argc);
  marshal("should fail to compile", argv);
  marshal("should fail to compile", &argc);
  marshal("should be std::string", argv[0]);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不知在何时 2025-01-24 07:37:15

灾难恢复之前 ,没有办法做到这一点。您也许可以使其在您的编译器上运行,但生成的程序格式不正确,无需诊断。

使用=删除

template< class T >
void marshal(std::string name, T  *value) = delete;

DR 2518 已解决,措辞已调整:

在 static_assert 声明中,常量表达式根据上下文转换为 bool,并且转换后的表达式应为常量表达式 (7.7 [expr.const])。如果如此转换后表达式的值为 true或者在模板定义的上下文中计算表达式,则声明无效。否则,static_assert 声明将失败,程序格式错误,并且生成的诊断消息 (4.1 [intro.compliance]) 应包含字符串文字的文本(如果提供)。

粗体文本表示模板定义中的static_assert(false, "whatever")而不是实例化不会导致您的程序违反了 C++ 标准。此外,要求所有模板都具有有效实例化的子句放宽了范围,以排除由 static_assert 引起的失败。

结果是修订后标准的示例:

模板
无效 f(T t) {
  if constexpr (sizeof(T) == sizeof(int)) {
    使用(t);
  } 别的 {
    static_assert(false, "必须是 int 大小");
  }
}

完全没问题,如果您使用非 int 大小的 T 调用 f,则会发生失败。

缺陷报告被视为追溯更改 - 因此符合要求的 编译器必须实现缺陷报告,并且可以这样做没有说他们正在使用该标准的“较新”版本。并非所有 编译器会这样做,因为大多数 C++ 编译器往往是不完整的实现标准,因为它们可能是在

Prior to a DR against , there was no way to do this. You might be able to make it work on your compiler, but the resulting program is ill formed no diagnostic required.

Use =delete.

template< class T >
void marshal(std::string name, T  *value) = delete;

After 's DR 2518 was resolved, the wording was tweaked:

In a static_assert-declaration, the constant-expression is contextually converted to bool and the converted expression shall be a constant expression (7.7 [expr.const]). If the value of the expression when so converted is true or the expression is evaluated in the context of a template definition, the declaration has no effect. Otherwise, the static_assert-declaration fails, the program is ill-formed, and the resulting diagnostic message (4.1 [intro.compliance]) should include the text of the string-literal, if one is supplied.

the bolded text means that a static_assert(false, "whatever") in a template definition but not instantiation doesn't cause your program to be in violation of the C++ standard. In addition, clauses that require all templates to have a valid instantiation where relaxed to exclude failures caused by static_asserts.

The result is this example from the amended standard:

template <class T>
void f(T t) {
  if constexpr (sizeof(T) == sizeof(int)) {
    use(t);
  } else {
    static_assert(false, "must be int-sized");
  }
}

being perfectly a-ok, with the failure occurring if you call f with a non-int sized T.

Defect reports are considered retroactive changes - so a conforming compiler must implement the defect report, and can do so without having to say they are using a 'newer' version of the standard. Not all compilers will do so, both because most C++ compilers tend to be incomplete implementations of a standard, and because they may have been written before was redefined.

木緿 2025-01-24 07:37:15

依赖矛盾确实不是最好的,但有一个更简单的方法:

template <class...>
struct False : std::bool_constant<false> { };

template <class T>
void bang() {
    static_assert(False<T>{}, "bang!");
}

为什么这不属于“无有效专业化”的情况?
好吧,因为你实际上可以通过代码的后半部分进行有效的特化:

template <>
struct False<int> : std::bool_constant<true> { };

int main() {
    bang<int>(); // No "bang"!
}

当然,没有人会真正特化 False 来真正打破你的断言代码,但这是可能的:)

Relying on a contradiction is not the best indeed, but there's a simpler way:

template <class...>
struct False : std::bool_constant<false> { };

template <class T>
void bang() {
    static_assert(False<T>{}, "bang!");
}

Why does this not fall under the "no valid specialization" case?
Well, because you can actually make a valid specialization, with that second half of the code:

template <>
struct False<int> : std::bool_constant<true> { };

int main() {
    bang<int>(); // No "bang"!
}

Of course, no one is actually going to specialize False to break your assertions in real code, but it is possible :)

似梦非梦 2025-01-24 07:37:15

根据 [temp.res]/8 (强调我的):

知道哪些名称是类型名称可以了解每个模板的语法
待检查。 程序格式错误,无需诊断,如果
- 无法为模板生成有效的专业化或模板中 constexpr if 语句的子语句以及
模板未实例化,或者 (...)

What you are trying to do is doomed to be ill-formed (even your workaround can fail) according to [temp.res]/8 (emphasis mine):

Knowing which names are type names allows the syntax of every template
to be checked. The program is ill-formed, no diagnostic required, if:
- no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the
template is not instantiated, or (...)

终陌 2025-01-24 07:37:15

我不明白为什么你有 template< T级>首先, void marshal(std::string name, T *value) 。这应该只是主模板中的 static_assert 。

也就是说,您应该将主模板的定义更改为

template< class T >
void marshal(std::string name, T value)
{
  static_assert(std::is_pod<T>::value);
  static_assert(!std::is_pointer<T>::value);
  std::cout<<name<<" is a POD type."<<std::endl;
}

I don't understand why you have template< class T > void marshal(std::string name, T *value) in the first place. This should just be a static_assert in the primary template.

That is, you should change the definition of your primary template to

template< class T >
void marshal(std::string name, T value)
{
  static_assert(std::is_pod<T>::value);
  static_assert(!std::is_pointer<T>::value);
  std::cout<<name<<" is a POD type."<<std::endl;
}
淡水深流 2025-01-24 07:37:15

事实上,我遇到了和你一样的问题。我的 static_assert(false) 在 gcc-9 中表现良好。
但是当我使用 gcc-7 作为我的编译器时,就会发生这种情况。所以最好的方法可能是升级你的 gcc 或编译器版本。

In fact, I meet the same problem as you.My static_assert(false) perform well in gcc-9.
But when I use gcc-7 as my compiler, it will happen. So the best way may be upgrade your gcc or compiler version.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文