与模板化的结构或类结合使用时,概念的正确语法是什么? [英] What is the correct syntax for concept in combination with a templated struct or class?

查看:103
本文介绍了与模板化的结构或类结合使用时,概念的正确语法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我正在使用 concept s为 template d struct定义不同的构造函数。以下是代码:

Recently I was using concepts to define different constructors for a templated struct. Here is the code:

#include <iostream>

namespace detail {
    template<typename T, typename U >
    concept SameHelper = std::is_same_v<T, U>;
}

template<typename T, typename U >
concept same_as = detail::SameHelper<T, U> && detail::SameHelper<U, T>;

template<typename T>
concept trivial = same_as<T, bool> || same_as<T, char>;

template<typename T>
concept not_trivial = !trivial<T>;

template<typename T>
struct Foo
{
    Foo(T t) requires trivial<T> : member{t} { std::cout << "Foo is trivial" <<std::endl; }
    Foo(const auto& t) requires not_trivial<T> : member{t} { std::cout << "Foo is not trivial" <<std::endl;}
    const T member;
};

template<typename T>
struct Bar
{
    Bar(auto t) requires trivial<T> : member{t} { std::cout << "Bar is trivial" <<std::endl; }
    Bar(const T& t) requires not_trivial<T> : member{t} { std::cout << "Bar is not trivial" <<std::endl;}
    const T member;
};

template<typename T>
struct Baz
{
    Baz(auto t) requires trivial<T> : member{t} { std::cout << "Baz is trivial" <<std::endl; }
    Baz(const auto& t) requires not_trivial<T> : member{t} { std::cout << "Baz is not trivial" <<std::endl;}
    const T member;
};

template<typename T>
struct Qux
{
    Qux(T t) requires trivial<T> : member{t} { std::cout << "Qux is trivial" <<std::endl; }
    Qux(const T& t) requires not_trivial<T> : member{t} { std::cout << "Qux is not trivial" <<std::endl;}
    const T member;
};

int main()
{
    Foo(true);
    Foo(3.14159);

    Bar(true);
    Bar(3.14159);

    //Baz(true);    // does not compile if uncommented
    //Baz(3.14159); // does not compile if uncommented

    //Qux(true);    // does not compile if uncommented
    //Qux(3.14159); // does not compile if uncommented

    return 0;
}

您可以在线运行以上代码
我想知道为什么Foo和Bar可以很好地编译,而Baz和Qux如果不加注释就不能编译。
恕我直言,Baz和Qux的语法更方便。

You can run the above code online. I am wondering why Foo and Bar compile fine, whereas Baz and Qux do not compile if uncommented. IMHO the syntax for Baz and Qux is mutch more convenient.

推荐答案

让我们依次浏览所有类模板。我将使用一个更简单的概念,因为 bool 是唯一相关的类型:

Let's go through all the class templates in order. I'm going to use a simpler concept, since bool is the only relevant type:

template <typename T>
struct Foo
{
    Foo(T) requires same_as<T, bool>;
    Foo(const auto&) requires (not same_as<T, bool>);
};

Foo(true);
Foo(3.14159);

在进行类模板参数推导时,过程是我们将所有构造函数都转化为函数-然后执行重载解析以找出最终要使用的特定类型。对于 Foo ,它们将变为:

When doing class template argument deduction, the process is that we take all the constructors and turn them into functions - and then perform overload resolution to figure out what specific type we end up at. For Foo, these would become:

template <typename T> requires same_as<T, bool>
auto __f(T) -> Foo<T>;

template <typename T> requires (not same_as<T, bool>)
auto __f(const auto&) -> Foo<T>;

__f(true);    // from Foo(true)
__f(3.14159); // from Foo(3.14159)

__ f的第一个重载 T 可以从其论证中推导出来。在第二个重载中,它不是-无法确定 T 是什么...因此,就CTAD流程而言,基本上没有关系。结果, __ f(true)很好(您得到 Foo< bool> ),但 __ f(3.14159)格式错误-第一个重载不可行,因为 double 不是 bool ,第二次重载是不可行的,因为未推导 T

In the first overload of __f, T is deducible from its argument. In the second overload, it is not - there is no way to determine what T is... so it basically doesn't matter, as far as the CTAD process goes. As a result, __f(true) is fine (you get back Foo<bool>) but __f(3.14159) is ill-formed - the first overload isn't viable because double isn't bool and the second overload isn't viable because T isn't deduced.

至少这是应该应遵循的规则。今天的措辞缺少将每个构造函数的约束传递到重载集中的规则,而clang恰好遵循此处规则的字母-其版本为 __ f 没有附加任何需求。但这绝对不是我们要在这里发生的事情,并且肯定是核心问题。另请参见 llvm错误#44484

At least that's what the rules should be. The wording as it exists today is missing the rule that we carry over the constraints from each constructor into the overload set, and clang happens to follow the letter of the rules here - its version of __f's don't have any requires attached to them. But this is definitely not what we want to happen here, and will certainly be a Core issue. See also llvm bug #44484.

Bar 与之类似,只是参数被翻转:

Bar is similar, just with the arguments flipped:

template<typename T>
struct Bar
{
    Bar(auto) requires same_as<T, bool>;
    Bar(const T&) requires (not same_as<T, bool>);
};

这里,唯一可以给我们CTAD答案的构造函数是第二个-但是第二个要求 T 不是 bool 。所以 Bar(true)格式不正确,但 Bar(3.14159)很好,可以给您 Bar< double>

Here, the only constructor that could give us an answer for CTAD is the second one - but the second one requires that T is not bool. So Bar(true) is ill-formed, but Bar(3.14159) is fine and gives you Bar<double>.

对于 Baz

template<typename T>
struct Baz
{
    Baz(auto) requires same_as<T, bool>;
    Baz(const auto&) requires (not same_as<T, bool>);
};

现在都不是构造函数参与CTAD,所以您必须编写演绎指导自己,以便在这里做任何事情。拒绝这些是正确的。

now neither constructor participates in CTAD, so you'd have to write a deduction guide yourself in order to do anything here. Rejecting these is correct.

Qux

template<typename T>
struct Qux
{
    Qux(T) requires same_as<T, bool>;
    Qux(const T&) requires (not same_as<T, bool>);
};

这里,两个构造函数都参加了CTAD,所以 Qux(true) Qux(3.14159)都可以正常工作(只是每个都选择不同的构造函数)。这与我们之前看到的行为是相同的-clang遵循规则,而gcc(和msvc)遵循规则。

Here, both constructors do participate in CTAD so Qux(true) and Qux(3.14159) both work fine (just each picks a different constructor). This is the same kind of behavior as we saw before - clang follows the rules as they are, while gcc (and msvc) follow the rules are they should be.

这篇关于与模板化的结构或类结合使用时,概念的正确语法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆