首页 > 解决方案 > 为什么此自定义类型特征无法编译

问题描述

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

如果我将 #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;
    
}

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

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, B, 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 &, std::variant<A, B, C> &)>, std::integer_sequence<unsigned long, 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 &, std::variant<A, B, 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 &, std::variant<A, B, 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::variant<A, B, C> &), 3>, 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::variant<A, B, C> &), 3>, 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 &, std::variant<A, B, 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>, ABVis &, std::variant<A, B, 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 &, std::variant<A, B, 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, B, C>]
    std::cout << can_visit<ABVis,ABC>::value << std::endl;
                 ^
<source>:47:18: note: in instantiation of template class 'can_visit<ABVis, std::variant<A, B, 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>, ABVis &, std::variant<A, B, 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>, ABVis &, std::variant<A, B, 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 &, std::variant<A, B, 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, B, C>]
    std::cout << can_visit<ABVis,ABC>::value << std::endl;
                 ^
<source>:47:18: note: in instantiation of template class 'can_visit<ABVis, std::variant<A, B, 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),
                 ^~~~~~~~~~~~~
/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, B, 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 &, std::variant<A, B, C> &)>, std::integer_sequence<unsigned long, 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 &, std::variant<A, B, 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 &, std::variant<A, B, 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::variant<A, B, C> &), 3>, 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::variant<A, B, C> &), 3>, 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 &, std::variant<A, B, 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>, ABVis &, std::variant<A, B, 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 &, std::variant<A, B, 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, B, C>]
    std::cout << can_visit<ABVis,ABC>::value << std::endl;
                 ^
<source>:47:18: note: in instantiation of template class 'can_visit<ABVis, std::variant<A, B, 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>, ABVis &, std::variant<A, B, 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>, ABVis &, std::variant<A, B, 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 &, std::variant<A, B, 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, B, C>]
    std::cout << can_visit<ABVis,ABC>::value << std::endl;
                 ^
<source>:47:18: note: in instantiation of template class 'can_visit<ABVis, std::variant<A, B, 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.
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, 
    B, 
    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

正如预期的那样。

标签: c++c++17sfinaedecltypevoid-t

解决方案


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

和表达

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

对所有TVisitorand都有效TVariant,因为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


1可能需要std::visitSFINAE'd,但我不知道足够的标准来解决这个问题。


推荐阅读