I'm not sure what I've done wrong below. I'm trying to write a trait can_visit and using the standard pattern of std::void_t and sfinae but it just won't compile and keeps telling me there is an error in the decltype statement containing the std::visit expression.
If I replace the line at #47 with
std::cout << can_visit<ABCVis,ABC>::value << std::endl;
it compiles but defeats the purpose.
std::cout << can_visit<ABVis,ABC>::value << std::endl;
should output 0 rather than failing to compile
#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;
}
The full error message is here but can be executed on godbolt at 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, 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
For context it is not much different than this code 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;
}
which does compile and outputs
1
0
as expected.
The detection idiom can only determine whether the given expression is valid.
And the expression
std::visit(std::declval<TVisitor&>(), std::declval<TVariant&>())
is valid for all TVisitor
and TVariant
, because std::visit
is an unrestricted template1.
However, the corresponding instantiation may not be valid, but this is not something you can detect directly.
Instead, you'll have to manually detect the conditions that may make the instantiation invalid. Your type trait should determine whether the given visitor can be called with all types in the given variant.
Like so, for instance:
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 It's possible that this and this require std::visit
to be SFINAE'd, but I don't know enough standardese to figure that out.