I am trying to implement my own std::variant
and stumbled across a problem.
The following code defines a struct A
which takes variadic template arguments. The class provides a method A::something()
which can only be called with a type which appears in it's template arguments. But the compiler always calls the method with the type which is specified first:
#include <iostream>
template <typename ...>
class A;
// Handle the edge case.
template <>
struct A<> { };
// Required so that `something()` can only be called with a type
// that appears in the template arguments.
template <typename T, typename ...TS>
struct A<T, TS...> : public A<TS...> {
void something(T) {
// Figure out which method was called.
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
int main() {
A<char, short> a;
int b = 10;
// Will always call `A<char, short>::something()` (with T = char).
a.something(b);
}
So I added another level of abstraction, allowing the user to specify the type explicitly:
#include <type_traits>
#include <iostream>
template <typename ...>
class A;
// Handle the edge case.
template <>
struct A<> { };
// Required so that `something()` can only be called with a type
// that appears in the template arguments.
template <typename T, typename ...TS>
struct A<T, TS...> : public A<TS...> {
// Allow user to explicitly specify T.
template <typename A = T>
void something(A, std::enable_if_t<std::is_same_v<A, T>>* = 0) {
// Figure out which method was called.
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
int main() {
A<char, short> a;
// Explicitly call `A<char, short>::something(char)`
a.something<char>(10); // works
// Explicitly call `A<short>::something(short)`
a.something<short>(10); // error
}
But the compiler isn't able to figure out which method to call (only the first one works):
main.cpp: In function ‘int main()’:
main.cpp:30:26: error: no matching function for call to ‘A<char, short int>::something<short int>(int)’
30 | a.something<short>(10); // error
| ^
main.cpp:17:10: note: candidate: ‘template<class A> void A<T, TS ...>::something(A, std::enable_if_t<is_same_v<A, T> >*) [with A = A; T = char; TS = {short int}]’
17 | void something(A, std::enable_if_t<std::is_same_v<A, T>>* = 0) {
| ^~~~~~~~~
main.cpp:17:10: note: template argument deduction/substitution failed
I guess the compiler isn't able to distinguish the methods and simply overrides the previous one when inheriting.
- Is my guess correct?
- How can I solve this problem?