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

为什么这个自定义类型特征不能编译

如何解决为什么这个自定义类型特征不能编译

我不确定我在下面做错了什么。我正在尝试编写一个特征 can_visit 并使用 std::void_t 和 sfinae 的标准模式,但它无法编译并一直告诉我包含 can_visit 的 decltype 语句中存在错误em>std::visit 表达式。

如果我将 #47 处的行替换为

std::cout << can_visit<ABCVis,ABC>::value << std::endl;

它编译但违背了目的。

std::cout << can_visit<ABVis,ABC>::value << std::endl;

应该输出 0 而不是编译失败

#include <variant>
#include <iostream>

template <class TVisitor,class tvariant,class=void>
struct can_visit: std::false_type {};

template <class TVisitor,class tvariant >
struct can_visit
<
    TVisitor,tvariant,std::void_t<
        decltype(std::visit(
            std::declval<TVisitor&>(),std::declval<tvariant&>()
            )
        )
    >
>
 : std::true_type {};

struct A{};
struct B{};
struct C{};

using ABC = std::variant<A,B,C>;
using AB = std::variant<A,B>;

struct ABCVis {
    void operator()(A const & a){};
    void operator()(B const & a){};
    void operator()(C const & a){};
};
struct ABVis {
    void operator()(A const & a){};
    void operator()(B const & a){};
};


int main(){
    std::cout << can_visit<ABVis,ABC>::value << std::endl;
    
}

完整的错误信息在这里,但可以在 Godbolt 上执行 https://godbolt.org/z/Gz4Ef4x1n

In file included from <source>:1:
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1020:11: error: no matching function for call to '__invoke'
          return std::__invoke(std::forward<_Visitor>(__visitor),^~~~~~~~~~~~~
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1031:29: note: in instantiation of member function 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(ABVis &,std::variant<A,C> &)>,std::integer_sequence<unsigned long,2>>::__visit_invoke' requested here
      { return _Array_type{&__visit_invoke}; }
                            ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:976:48: note: in instantiation of member function 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(ABVis &,2>>::_S_apply' requested here
                std::index_sequence<__indices...,__index>>::_S_apply();
                                                             ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:955:7: note: in instantiation of function template specialization 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(ABVis &,C> &),3>,std::integer_sequence<unsigned long>>::_S_apply_single_alt<false,2,std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(ABVis &,C> &)>>' requested here
            (_S_apply_single_alt<false,__var_indices>(
             ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:940:2: note: in instantiation of function template specialization 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(ABVis &,std::integer_sequence<unsigned long>>::_S_apply_all_alts<0,1,2>' requested here
        _S_apply_all_alts(
        ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1042:59: note: in instantiation of member function 'std::__detail::__variant::__gen_vtable_impl<std::__detail::__variant::_Multi_array<std::__detail::__variant::__deduce_visit_result<void> (*)(ABVis &,std::integer_sequence<unsigned long>>::_S_apply' requested here
        = __gen_vtable_impl<_Array_type,std::index_sequence<>>::_S_apply();
                                                                 ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1699:45: note: in instantiation of static data member 'std::__detail::__variant::__gen_vtable<std::__detail::__variant::__deduce_visit_result<void>,ABVis &,C> &>::_S_vtable' requested here
        _Result_type,_Visitor&&,_Variants&&...>::_S_vtable;
                                                   ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1718:19: note: in instantiation of function template specialization 'std::__do_visit<std::__detail::__variant::__deduce_visit_result<void>,C> &>' requested here
      return std::__do_visit<_Tag>(std::forward<_Visitor>(__visitor),^
<source>:13:23: note: in instantiation of function template specialization 'std::visit<ABVis &,C> &>' requested here
        decltype(std::visit(
                      ^
<source>:47:18: note: during template argument deduction for class template partial specialization 'can_visit<TVisitor,tvariant>' [with TVisitor = ABVis,tvariant = std::variant<A,C>]
    std::cout << can_visit<ABVis,ABC>::value << std::endl;
                 ^
<source>:47:18: note: in instantiation of template class 'can_visit<ABVis,C>>' requested here
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/bits/invoke.h:89:5: note: candidate template ignored: substitution failure [with _Callable = ABVis &,_Args = <C &>]: no type named 'type' in 'std::__invoke_result<ABVis &,C &>'
    __invoke(_Callable&& __fn,_Args&&... __args)
    ^
In file included from <source>:1:
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1041:36: error: constexpr variable '_S_vtable' must be initialized by a constant expression
      static constexpr _Array_type _S_vtable
                                   ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1699:45: note: in instantiation of static data member 'std::__detail::__variant::__gen_vtable<std::__detail::__variant::__deduce_visit_result<void>,C>>' requested here
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:955:7: note: subexpression not valid in a constant expression
            (_S_apply_single_alt<false,__var_indices>(
             ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:940:2: note: in call to '_S_apply_all_alts(__vtable,{})'
        _S_apply_all_alts(
        ^
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1042:4: note: in call to '_S_apply()'
        = __gen_vtable_impl<_Array_type,std::index_sequence<>>::_S_apply();
          ^
2 errors generated.
ASM generation compiler returned: 1
In file included from <source>:1:
/opt/compiler-explorer/gcc-10.3.0/lib/gcc/x86_64-linux-gnu/10.3.0/../../../../include/c++/10.3.0/variant:1020:11: error: no matching function for call to '__invoke'
          return std::__invoke(std::forward<_Visitor>(__visitor),std::index_sequence<>>::_S_apply();
          ^
2 errors generated.
Execution build compiler returned: 1

对于上下文,它与此代码没有太大区别 https://godbolt.org/z/aa5cqWhz8

#include <variant>
#include <iostream>

template <class A,class B,class=void>
struct can_add: std::false_type {};

template <class A,class B >
struct can_add
<
    A,std::void_t<
        decltype(std::declval<A>()+std::declval<B>()
        )
    >
>
 : std::true_type {};


int main(){


    std::cout << can_add<int,int>::value << std::endl;
    std::cout << can_add<int,std::string>::value << std::endl;

    
}

编译并输出

1
0

正如预期的那样。

解决方法

检测习语只能判断给定的表达式是否有效。

还有表情

std::visit(std::declval<TVisitor&>(),std::declval<TVariant&>())

对所有 TVisitorTVariant 都有效,因为 std::visit 是一个不受限制的模板1

但是,相应的实例化可能无效,但这不是您可以直接检测到的。

相反,您必须手动检测可能使实例化无效的条件。您的类型特征应该确定是否可以使用给定变体中的所有类型调用给定访问者。

像这样,例如:

template <class TVisitor,class TVariant>
struct can_visit;

template <class TVisitor,class... TTypes>
struct can_visit<TVisitor,std::variant<TTypes...>> {
    constexpr static bool value = (std::is_invocable_v<TVisitor,TTypes> && ...);
};

https://godbolt.org/z/fz7e5W7ah


1thisthis 可能需要 std::visit 被 SFINAE,但我不知道足够的标准来解决这个问题。

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