C++中是否有类似模板化的静态断言的东西?

huangapple go评论81阅读模式
英文:

Is there something like templated static_asserts in C++?

问题

以下是翻译好的部分:

可以检查类成员函数是否存在。可以从此答案中找到一个检查的实现:https://stackoverflow.com/a/257382/2492801。

现在可以使用 static_assert 来确保某个类具有所需方法的实现。但是,如果类是模板化的,我不知道是否可以进行这种静态断言,以及如何进行 - 除非我为具体的模板参数选择执行 static_assert。后者是可能的,但感觉不对...

请考虑以下代码:

#include <iostream>;

// SFINAE测试
template <typename T>
class has_helloworld
{
    typedef char one;
    struct two
    {
        char x[2];
    };

    template <typename C>
    static one test(decltype(&C::helloworld));
    template <typename C>
    static two test(...);

  public:
    enum
    {
        value = sizeof(test<T>(0)) == sizeof(char)
    };
};

template <int number>
struct Hello
{
    int helloworld()
    {
        return 0;
    }

    // 由于类Hello尚未完整,下一行不可能出现。
    // static_assert(has_helloworld<Hello<number>>::value);
};

template <int number>
struct Generic
{
};

int main()
{
    // 这是可能的,但我不喜欢它
    static_assert(has_helloworld<Hello<3>>::value);

    // 下面两行不可能出现,因为模板声明不能出现在块作用域中。
    // template <int number>
    // static_assert(has_helloworld<Hello<number>>::value);

    std::cout << has_helloworld<Hello<2>>::value << std::endl;
    std::cout << has_helloworld<Generic<2>>::value << std::endl;

    return 0;
}

这里是一个Godbolt链接:https://godbolt.org/z/c3bKKMxcc

在C++中是否有可能执行“模板静态断言”,即检查依赖于模板参数的类属性的 static_assert,而不选择该参数的虚拟值?

澄清:在实际情况下,模板参数在我的情况中并不重要,它只会妨碍 static_assert。因此,就像代码示例中一样,所有模板结构 Hello 都具有所需的方法,而不考虑参数 number

英文:

It is possible to check for the existence of class member functions. An implementation of the check could be taken from this answer: https://stackoverflow.com/a/257382/2492801.

Now a static_assert can be used to ensure that a certain class has an implementation of a needed method. But if the class is templated, I do not know whether such a static assertion is possible or how to do it - except if I do the static_assert for a concrete template parameter selection. The latter is possible, but it feels wrong...

Please consider the following code:

#include &lt;iostream&gt;

// SFINAE test
template &lt;typename T&gt;
class has_helloworld
{
    typedef char one;
    struct two
    {
        char x[2];
    };

    template &lt;typename C&gt;
    static one test(decltype(&amp;C::helloworld));
    template &lt;typename C&gt;
    static two test(...);

  public:
    enum
    {
        value = sizeof(test&lt;T&gt;(0)) == sizeof(char)
    };
};

template &lt;int number&gt;
struct Hello
{
    int helloworld()
    {
        return 0;
    }

    // The next line is not possible since class Hello is not complete yet.
    // static_assert(has_helloworld&lt;Hello&lt;number&gt;&gt;::value);
};

template &lt;int number&gt;
struct Generic
{
};

int main()
{
    // This is possible, but I don&#39;t like it
    static_assert(has_helloworld&lt;Hello&lt;3&gt;&gt;::value);

    // The next two lines are not possible because a template declaration cannot appear at block scope.
    // template &lt;int number&gt;
    // static_assert(has_helloworld&lt;Hello&lt;number&gt;&gt;::value);

    std::cout &lt;&lt; has_helloworld&lt;Hello&lt;2&gt;&gt;::value &lt;&lt; std::endl;
    std::cout &lt;&lt; has_helloworld&lt;Generic&lt;2&gt;&gt;::value &lt;&lt; std::endl;

    return 0;
}

Here is a Godbolt link: https://godbolt.org/z/c3bKKMxcc

Is there a possibility in C++ to do a "templated static assert", so a static_assert where I check a property of a class that depends on a template parameter without choosing a dummy value for that parameter?

Clarification: In my case in practice the template parameter does not play a role, it just impedes the static_assert. So like in the code example, all template structs Hello have the required method regardless of the parameter number.

答案1

得分: 4

你可以通过一个有毒的类型来实现某些功能:

template <int N>
struct assert_hello_has_helloworld_impl
{
    static_assert(has_helloworld<Hello<N>>::value);
};

为蛋糕添加糖:

template <int N>
void assert_hello_has_helloworld(assert_hello_has_helloworld_impl<N> = {}) {}
// ...
assert_hello_has_helloworld<3>();

完整演示:https://godbolt.org/z/7z9nzPqWs

英文:

You can achieve something with a poisoned type:

template &lt;int N&gt;
struct assert_hello_has_helloworld_impl
{
    static_assert(has_helloworld&lt;Hello&lt;N&gt;&gt;::value);
};

Add sugar to the cake:

template &lt;int N&gt;
void assert_hello_has_helloworld(assert_hello_has_helloworld_impl&lt;N&gt; = {}) {}
// ...
assert_hello_has_helloworld&lt;3&gt;();

Full demo: https://godbolt.org/z/7z9nzPqWs

答案2

得分: 2

以下是翻译好的部分:

"问题在于每个模板都可以有模板特化,并且只能对特定类型执行检查。作为一种解决方法,您可以将此参数隐藏在验证函数存在的代码内部:

template <template <std::size_t> typename T, typename = void>
struct has_helloworld : std::false_type { };

template <template <std::size_t> typename T>
struct has_helloworld<T, decltype(std::declval<T<0>>().helloworld())> : std::true_type { };

template <template <std::size_t> typename T>
constexpr bool has_helloworld_v = has_helloworld<T>::value;

所以现在静态断言不会明确指定参数:

static_assert(!has_helloworld_v<NoHelloworld>);
static_assert(has_helloworld_v<WithFuncHelloworld>);

这里有一个演示:https://godbolt.org/z/E8r7eTqde

问题在于这只检查了模板在参数 0(或其他预定义参数列表)上的安装情况。这使得这段代码变得脆弱。"

英文:

It is unclear what you are exactly expect.
I understand your question that: you wish to check if template has some function without explicitly specifying template parameter in this check.

Problem is that every template can have template specialization, and check can be performed only of specific type. As a workaround you can hide this parameter inside of code which verifies function existence:

template &lt;template &lt;std::size_t&gt; typename T, typename = void&gt;
struct has_helloworld : std::false_type { };

template &lt;template &lt;std::size_t&gt; typename T&gt;
struct has_helloworld&lt;T, decltype(std::declval&lt;T&lt;0&gt;&gt;().helloworld())&gt; : std::true_type { };

template &lt;template &lt;std::size_t&gt; typename T&gt;
constexpr bool has_helloworld_v = has_helloworld&lt;T&gt;::value;

So now static assertion do not explicitly states parameters:

static_assert(!has_helloworld_v&lt;NoHelloworld&gt;);
static_assert(has_helloworld_v&lt;WithFuncHelloworld&gt;);

Here is a demo: https://godbolt.org/z/E8r7eTqde

Problem is that this only check installation of template for parameter 0 (or other predefined list of parameters). This makes this code fragile.

答案3

得分: 2

模板类不是类。

模板类生成类。

询问模板类是否具有特定成员是一个类别错误。这就像询问特定类型的纸上是否画有山一样。

你可能会说:“但我知道这张纸总是用来绘制落基山地图的!” C++ 不知道这一点,它只知道你在谈论纸。

将模板类加参数映射到类的计算是一个图灵完备的计算。在一般情况下,这个过程不能被反转 - 对于一个模板类 X&lt;A&gt;,你不能回答问题:“对于任意的 AX&lt;&gt; 有什么属性”。

在设计C++的模板系统时,选择了一个无法被反转的计算模型。

事实上,模板的最典型用法使用了一组远弱于宏的计算能力 - 大多数模板的使用都与基本的元类型检查有一定的距离。

举个具体的例子,可以编写一个模板类,使得 Hello&lt;N&gt; 只有在常数 N 的Collatz猜想为真且接受bignums作为参数时才具有特定属性。

要知道 Hello&lt;?&gt; 对于任意参数的属性,编译器必须证明Collatz猜想。

现在,你可能会说:“我不这么做”。太糟糕了;C++ 不会说:“只要编写模板的人不做任何花哨的事情,你就可以反转模板映射并解决这个问题”。

它可以;它可以定义一组更弱的模板机制,可以安全地反转而不会遇到这个问题。许多语言正是这样做的(例如Java中的泛型,它既减弱了你可以进行的映射类型,而且有一个相当不同的实现方式;C#的版本比Java略强,但也以一种与C++模板类似的方式减弱了)。

英文:

Template classes are not classes.

Template classes generate classes.

Asking if a template class has a specific member is a category error. It is like asking if a specific type of paper has mountains drawn on it.

You might say "but I know that this paper is always used to draw maps of the rockies!" C++ doesn't know this, it just knows you are talking about paper.

The mapping of template class plus parameters to class is a Turing complete computation. This process cannot be inverted in the general case - for a template class X&lt;A&gt;, you cannot answer the question "what properties does X&lt;&gt; have for an arbitrary A".

When designing the template system for C++, the computational model chosen was too strong to be inverted like that.

It is true that the most typical use of templates uses a far weaker set of computational capabilities - most uses of templates are a step removed from macros with basic meta type checking.

As a concrete example, a template class can be written such that Hello&lt;N&gt; has a specific property if and only if the Collatz conjecture is true for the constant N, and it accepts bignums as arguments.

In order to know the properties of Hello&lt;?&gt; for an arbitrary argument, the compiler would have to prove the Collatz conjecture.

Now, you might say, "I'm not doing that". Too bad; C++ doesn't say "so long as the person writing the template doesn't do anything fancy, you can invert the template mapping and solves this problem".

It could; it could define a weaker set of template mechanics that can be safely inverted without hitting this problem. A number of languages do exactly that (Generics in Java, which both weaken the kind of mapping you can do and have a rather different implementation; C#'s version is a bit stronger than Java, but also weakens C++ templates in a somewhat similar way).

huangapple
  • 本文由 发表于 2023年7月17日 23:47:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706149.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定