With this code below:
#define P std::cout << __PRETTY_FUNCTION__ << '\n'
template <typename ... arg_pack> concept is_tuple = requires(std::tuple<arg_pack ...>) { true; };
template <typename ... arg_pack> concept is_variant = requires(std::variant<arg_pack ...>) { true; };
template <is_tuple type_t> void X(const type_t &t) { P; } // A
//template <is_variant type_t> void X(const type_t &v) { P; } // B
void X(int) { P; }
int main()
{
std::tuple<int> t;
std::variant<int> v;
X(t);
X(v);
X(0);
return 0;
}
I'm getting this output:
void X(const type_t &) [type_t = std::tuple<int>] void X(const type_t &) [type_t = std::variant<int>] void X(int)
Uncommenting the line marked with B comment, the compiler reports ambiguity:
error: call to 'X' is ambiguous
X(t);
^
note: candidate function [with type_t = std::tuple<int>]
template <is_tuple type_t> void X(const type_t &t) { P; }
^
note: candidate function [with type_t = std::tuple<int>]
template <is_variant type_t> void X(const type_t &v) { P; }
^
error: call to 'X' is ambiguous
X(v);
^
note: candidate function [with type_t = std::variant<int>]
template <is_tuple type_t> void X(const type_t &t) { P; }
^
note: candidate function [with type_t = std::variant<int>]
template <is_variant type_t> void X(const type_t &v) { P; }
^
2 errors generated.
This is very confusing.
- Why calling
XwithBline commented doesn't report a compiler error but calls the incorrect function? - Why calling
XwithBline uncommented is abiguous considering thatstd::is_same_v<std::tuple<int>, std::variant<int>>is false?
Thanks Jan Schultke and SO community, I've managed to workaround a simple is_tuple and is_variant with your help:
template <typename type_t>
concept is_tuple = []<typename ... arg_pack>(std::tuple<arg_pack ...>){ return true; }(type_t{});
template <typename type_t>
concept is_variant = []<typename ... arg_pack>(std::variant<arg_pack ...>){ return true; }(type_t{});
The call is ambiguous because you're misusing concepts, and both your concepts are effectively
concept C = true;In the concept:
... the
std::tuple<arg_pack>isn't verifying that you somehow have astd::tuple; thestd::tuple<arg_pac>is like an unused function parameter in the requires expression.The type constraint
<is_tuple type_t>requiresis_tuple<type_t>, which expands to:Ideally, you should create a concept for a tuple-like type, or a variant-like type. This is what the C++ standard library commonly does, and such concepts exists in the C++ standard, although they are exposition-only. See also: C++20 Concept to check tuple-like types
With a tuple-like concept, you will be much more flexible, and you can accept types like
std::array,std::pair, etc. instead of juststd::tuple.