Search code examples
c++templatesc++11enable-if

Using std::enable_if and variadic base classes


Here's a cut down example of what I'm trying to do:

#include <string>
#include <iostream>
#include <type_traits>

template <typename T>
class foo
{
public:
    template <typename U>
    typename std::enable_if<std::is_same<T, U>::value>::type
    bar(const U& t)
    {
        std::cout << t << "\n";
    }
};

template <typename... Args>
class baz
  : public foo<Args>...
{
    
};

int main()
{
    baz<double, std::string> b;
    b.bar(1.0);
}

This gives me ambiguous function errors:

error: request for member 'bar' is ambiguous

b.bar(1.0);

note: candidates are: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = std::basic_string<char>]

note: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = double]

My questions are twofold:

  1. Why is the inner template U not deduced? I'm supposing that it's due to ordering of template deduction and overload resolution, but can someone explain this?
  2. Is there another way of going about what I'm trying to do?

Solution

  • I think the error message is misleading. The problem is actually name bar is available in multiple base classes and you've not used using directive to bring the names you want into the derived class scope.

    Here is one working solution:

    template <typename X, typename... Args>
    class baz : public foo<X>, public baz<Args...>
    {
        public:
            using foo<X>::bar;        //bring the name from the first base
            using baz<Args...>::bar;  //bring the name from the second base
    };
    
    template <typename X>
    class baz<X> : public foo<X>   //specialization for one argument
    {
            //no using directive needed, as there is one base only!
    };
    

    Complete Demo