2

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.

  1. Is my guess correct?
  2. How can I solve this problem?
asynts
  • 2,213
  • 2
  • 21
  • 35
  • 3
    `using A::something;`? Otherwise each next `something` hides one in the base. – Evg Sep 10 '18 at 16:13

0 Answers0