Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Valid code fail to be compiled by visual studio 2015 (std function bug?)

I've been working with some code recently in c++11. This code is working fine in GCC and Clang, and I've used it extensively all across my project. Now, I need to make it work in MSVC. All the c++11 features I need are marked as yes. However, this sample of code simply refuses to build. I tried to fix errors but I did not found a solution yet. This is the sample:

#include <functional>
#include <iostream>
#include <type_traits>

template<typename T>
struct Provider final {
    Provider() = delete;
    Provider(const Provider& other) : _callback{ other._callback } {}
    Provider(Provider&& other) : _callback{ std::move(other._callback) } {}

    Provider& operator=(Provider&& other) {
        std::swap(other._callback, _callback);
        return *this;
    }

    Provider& operator=(const Provider& other) {
        _callback = other._callback;
        return *this;
    }

    template<typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type, typename = typename std::enable_if<!std::is_same<U, T>::value>::type>
    Provider<T>& operator=(Provider<U>&& other) {
        std::swap(other._callback, _callback);
        return *this;
    }

    template<typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type, typename = typename std::enable_if<!std::is_same<U, T>::value>::type>
    Provider<T>& operator=(const Provider<U>& other) {
        _callback = other._callback;
        return *this;
    }

    template<typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type, typename = typename std::enable_if<!std::is_same<U, T>::value>::type>
    Provider(const Provider<U>& other) : _callback{ other._callback } {}

    template<typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value>::type, typename = typename std::enable_if<!std::is_same<U, T>::value>::type>
    Provider(Provider<U>&& other) : _callback{ std::move(other._callback) } {}

    template<typename U, typename = typename std::enable_if<std::is_constructible<std::function<T()>, U>::value>::type>
    Provider(U callback) : _callback{ callback } {}

    template<typename = typename std::enable_if<!std::is_constructible<std::function<T()>, T>::value, T>::type>
    Provider(T value) : _callback{[=] { return value; }} {}

    template<typename U>
    friend struct Provider;

    T operator()() const {
        return _callback();
    }

private:
    std::function<T()> _callback;
};

template<typename T>
void doSomething(Provider<T> p) {
    std::cout << "My value is:" << p() << std::endl;
}

int main()
{
    Provider<int> p1 = 9;
    Provider<double> p2 = [] { return 9.4; };
    Provider<unsigned int> p3{9};
    Provider<float> p4{[]{ return 9.4f; }};

    doSomething<unsigned int>(5);
    doSomething<float>([] { return 9.5f; });
    doSomething<int>(p1);
    doSomething<double>(p2);

    return 0;
}

It is supposed to output this:

My value is:5
My value is:9.5
My value is:9
My value is:9.4

But instead, the compiler output throw this at me:

1>------ Build started: Project: test1, Configuration: Debug Win32 ------
1>  test1.cpp
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1501): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1501): note: With the following template arguments:
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1501): note: '_Callable=_Decayed &'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1501): note: '_Types={}'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(210): note: see reference to function template instantiation '_Rx std::_Invoke_ret<_Rx,_Callable&>(std::_Forced<_Rx,false>,_Callable &)' being compiled
1>          with
1>          [
1>              _Rx=unsigned int,
1>              _Callable=_Decayed
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(208): note: while compiling class template member function 'unsigned int std::_Func_impl<_Decayed,_Alloc,_Ret>::_Do_call(void)'
1>          with
1>          [
1>              _Alloc=std::allocator<int>,
1>              _Ret=unsigned int
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(136): note: see reference to class template instantiation 'std::_Func_impl<_Decayed,_Alloc,_Ret>' being compiled
1>          with
1>          [
1>              _Alloc=std::allocator<int>,
1>              _Ret=unsigned int
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(339): note: see reference to class template instantiation 'std::_Is_large<_Myimpl>' being compiled
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(318): note: see reference to function template instantiation 'void std::_Func_class<_Ret>::_Reset_alloc<_Ty,std::allocator<_Ty>>(_Fx &&,const _Alloc &)' being compiled
1>          with
1>          [
1>              _Ret=unsigned int,
1>              _Ty=int,
1>              _Fx=int,
1>              _Alloc=std::allocator<int>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(318): note: see reference to function template instantiation 'void std::_Func_class<_Ret>::_Reset_alloc<_Ty,std::allocator<_Ty>>(_Fx &&,const _Alloc &)' being compiled
1>          with
1>          [
1>              _Ret=unsigned int,
1>              _Ty=int,
1>              _Fx=int,
1>              _Alloc=std::allocator<int>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(484): note: see reference to function template instantiation 'void std::_Func_class<_Ret>::_Reset<int>(_Fx &&)' being compiled
1>          with
1>          [
1>              _Ret=unsigned int,
1>              _Fx=int
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\functional(484): note: see reference to function template instantiation 'void std::_Func_class<_Ret>::_Reset<int>(_Fx &&)' being compiled
1>          with
1>          [
1>              _Ret=unsigned int,
1>              _Fx=int
1>          ]
1>  c:\users\master\documents\visual studio 2015\projects\test1\test1\test1.cpp(43): note: see reference to function template instantiation 'std::function<T (void)>::function<U>(_Fx)' being compiled
1>          with
1>          [
1>              T=unsigned int,
1>              U=int,
1>              _Fx=int
1>          ]
1>  c:\users\master\documents\visual studio 2015\projects\test1\test1\test1.cpp(43): note: see reference to function template instantiation 'std::function<T (void)>::function<U>(_Fx)' being compiled
1>          with
1>          [
1>              T=unsigned int,
1>              U=int,
1>              _Fx=int
1>          ]
1>  c:\users\master\documents\visual studio 2015\projects\test1\test1\test1.cpp(68): note: see reference to function template instantiation 'Provider<unsigned int>::Provider<int,void>(U)' being compiled
1>          with
1>          [
1>              U=int
1>          ]
1>  c:\users\master\documents\visual studio 2015\projects\test1\test1\test1.cpp(68): note: see reference to function template instantiation 'Provider<unsigned int>::Provider<int,void>(U)' being compiled
1>          with
1>          [
1>              U=int
1>          ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

This is the exact same code on ideone, compiling and executing successfully: https://ideone.com/aANd04

Thanks in advance!

like image 327
Guillaume Racicot Avatar asked Aug 18 '15 16:08

Guillaume Racicot


1 Answers

VS 2015 does not (yet) support expression SFINAE, so you can't use std::is_constructible on std::function. (Note that this is strictly speaking a C++14 feature; C++11 std::function construction is not SFINAE-enabled.)

From http://blogs.msdn.com/b/vcblog/archive/2015/04/29/c-11-14-17-features-in-vs-2015-rc.aspx:

We're planning to start implementing Expression SFINAE in the compiler immediately after 2015 RTM, and we're planning to deliver it in an Update to 2015, supported for production use. (But not necessarily 2015 Update 1. It might take longer.)

like image 134
ecatmur Avatar answered Oct 17 '22 20:10

ecatmur