1

I have a following use case which is causing clang to emit errors.

#include <iostream>
#include <type_traits>

class A
{
    public:
        int x;
        A(int g):x(g){}
};

class B : public A
{
    public:
        int y;
        B(int p):A(p),y(p) {}
};

class Holder
{
    template<typename T>
        Holder(typename std::enable_if<!std::is_same<T,A>::value
                     && std::is_base_of< A , T>::value >::type&& val)
        : b (std::forward<T>(val))
        {}


    private:
        B b;
};

int main()
{
    B b(10);

    Holder h(b);
}

The error is as follows

forward.cpp:35:12: error: no matching constructor for initialization of 'Holder'
    Holder h(b);
           ^ ~
forward.cpp:18:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const Holder' for 1st argument
class Holder
      ^
forward.cpp:18:7: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'Holder' for 1st argument
class Holder
      ^
forward.cpp:21:9: note: candidate template ignored: couldn't infer template argument 'T'
        Holder(typename std::enable_if<!std::is_same<T,A>::value
        ^
1 error generated.

when compiled with clang++ --std=c++11 forward.cpp.

What am I missing here?

I have already consulted this and this questions, however they didnt seem to solve my use case.

Community
  • 1
  • 1
Recker
  • 1,915
  • 25
  • 55
  • 1
    The important message is the last one. The compiler can't infer T. – MikeMB May 06 '16 at 07:38
  • 2
    You should read about [non-deduced context](http://stackoverflow.com/questions/25245453/what-is-a-nondeduced-context?lq=1) to see why the compiler cannot deduce `T` from `B`. – Bo Persson May 06 '16 at 07:39

1 Answers1

4

You forget to specify the second parameter of std::enable_if (which defaults to void)

template<typename T>
Holder(typename std::enable_if<!std::is_same<T, A>::value
                 && std::is_base_of< A , T>::value, T>::type&& val)
    : b (std::forward<T>(val))
    {}

But anyway, T would be in a non deducible context. You may do

template<typename T>
Holder(T&& val,
       typename std::enable_if<!std::is_same<typename std::decay<T>::type, A>::value
                               && std::is_base_of<A, typename std::decay<T>::type>::value,
                               std::nullptr_t>::type = nullptr)
    : b (std::forward<T>(val))
    {}

or

template<typename T,
         typename std::enable_if<!std::is_same<typename std::decay<T>::type, A>::value
                               && std::is_base_of<A, typename std::decay<T>::type>::value,
                               std::nullptr_t>::type = nullptr>
Holder(T&& val)
    : b (std::forward<T>(val))
    {}
Jarod42
  • 203,559
  • 14
  • 181
  • 302