我正在尝试使用 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]);
}
发布评论
评论(5)
在灾难恢复之前 c++23,没有办法做到这一点。您也许可以使其在您的编译器上运行,但生成的程序格式不正确,无需诊断。
使用
=删除
。c++23 的 DR 2518 已解决,措辞已调整:
粗体文本表示模板定义中的
static_assert(false, "whatever")
而不是实例化不会导致您的程序违反了 C++ 标准。此外,要求所有模板都具有有效实例化的子句放宽了范围,以排除由static_assert
引起的失败。结果是修订后标准的示例:
完全没问题,如果您使用非
int
大小的T
调用f
,则会发生失败。缺陷报告被视为追溯更改 - 因此符合要求的 c++23 编译器必须实现缺陷报告,并且可以这样做没有说他们正在使用该标准的“较新”版本。并非所有 c++23 编译器会这样做,因为大多数 C++ 编译器往往是不完整的实现标准,因为它们可能是在 c++23。
Prior to a DR against c++23, 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
.After c++23's DR 2518 was resolved, the wording was tweaked:
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 bystatic_assert
s.The result is this example from the amended standard:
being perfectly a-ok, with the failure occurring if you call
f
with a non-int
sizedT
.Defect reports are considered retroactive changes - so a conforming c++23 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 c++23 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 c++23 was redefined.
依赖矛盾确实不是最好的,但有一个更简单的方法:
为什么这不属于“无有效专业化”的情况?
好吧,因为你实际上可以通过代码的后半部分进行有效的特化:
当然,没有人会真正特化
False
来真正打破你的断言代码,但这是可能的:)Relying on a contradiction is not the best indeed, but there's a simpler way:
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:
Of course, no one is actually going to specialize
False
to break your assertions in real code, but it is possible :)根据 [temp.res]/8 (强调我的):
What you are trying to do is doomed to be ill-formed (even your workaround can fail) according to [temp.res]/8 (emphasis mine):
我不明白为什么你有
template< T级>首先, void marshal(std::string name, T *value)
。这应该只是主模板中的 static_assert 。也就是说,您应该将主模板的定义更改为
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
事实上,我遇到了和你一样的问题。我的 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.