为什么此自定义类型特征无法编译

Why does this custom type trait not compile

我不确定我在下面做错了什么。我正在尝试编写特征 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

符合预期。

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

和表达式

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


1 有可能 this and this 要求 std::visit 是 SFINAE'd,但我不知道足够的标准来解决这个问题。