微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么以下 3 个版本中的 2 个版本 std::visit 不起作用

如何解决为什么以下 3 个版本中的 2 个版本 std::visit 不起作用

我试图使用 std::visit 访问变体中的成员,如果该成员不可用则抛出错误

我能够得到一个可行的解决方案,但我发现前两次尝试中的错误无法理解。

有人知道为什么“版本 1”和“版本 2”不起作用?

#include <variant>
#include <vector>
#include <stdexcept>

struct a
{
    int value=32;
};

struct b : a
{

};

struct c
{
    //empty
};

using t = std::variant<std::monostate,a,b,c>;

struct wacky_visitor{
    // Version 1 (doesn't work)
    // bool operator()(const auto& l,const auto& r)
    // { 
    //     throw std::runtime_error("bad"); 
    // };
    // Version 2 (doesn't work)
    // template <typename L,typename R> 
    // bool operator()(const L& l,const R& r)
    // { 
    //     throw std::runtime_error("bad"); 
    // };
    // Version 3 (works)
    template <typename L,typename R> 
    std::enable_if_t<!(std::is_base_of_v<a,L> && std::is_base_of_v<a,R>),bool> operator()(const L& l,const R& r)
    { 
        throw std::runtime_error("bad"); 
    };
    //Shared
    template <typename L,typename R> 
    std::enable_if_t<std::is_base_of_v<a,R>,const R& r)
    { 
        return l.value < r.value;
    };
};

int main()
{
    std::vector<t> foo_bar = {a(),b()};
        const auto comparison = [](const t &lhs,const t &rhs) {
        return std::visit(wacky_visitor{},lhs,rhs);
    };
    std::sort(foo_bar.begin(),foo_bar.end(),comparison);
    return 0;
}

https://godbolt.org/z/1c488v

解决方法

你的版本 1 和版本 2 的意思完全一样,所以我只考虑版本 2。

当您调用 wacky_visitor 时,您有两个重载选择:

// first overload
template <typename L,typename R>
bool operator()(L const&,R const&);

// second overload
template <typename L,typename R>
???? operator()(const L& l,const R& r)

????enable_if 的“约束”(我使用引号是因为它是 C++17 可以做的最好的约束,但它不是一个合适的约束,见下文) .在某些情况下,这是一种无效类型,因此将不考虑重载。但是如果它一个有效的类型,那么……好吧,我们的两个重载完全一样。两者在两个参数中都是完全匹配的,没有任何东西可以区分它们。

您的第三个版本有效,因为否定的 enable_if 条件确保两个重载中的一个是可行的,因此重载解决方案始终只有一个可供选择的候选者 - 然后成为最佳选择。


使用 if constexpr 并有一个重载更容易:

template <typename L,typename R> 
bool operator()(const L& l,const R& r)
{ 
    if constexpr (std::is_base_of_v<a,L> && std::is_base_of_v<a,R>) {
        return l.value < r.value;
    } else {
        throw std::runtime_error("bad");
    }
};

在 C++20 中,Concepts 具有附加功能,即 受约束 函数模板比​​不受约束的函数模板更受欢迎。这意味着你可以这样写:

// first overload as before,whichever syntax
template <typename L,R const&);

// second overload is now constrained
template <typename L,typename R> 
    requires std::is_base_of_v<a,R>
bool operator()(const L& l,const R& r);

如果第二个重载不可行,则调用第一个重载——和以前一样。但是现在,如果第二个重载可行,那么无论如何它都可以优先于第一个。

第二个重载也可以这样写:

template <std::derived_from<a> L,std::derived_from<a> R>
bool operator()(const L& l,const R& r);

意思大致相同。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。