I have a C++ template function being used in VS2013 without any problem. But when I upgrade to VS2017 the VC compiler complains it cannot match the argument list. Anyone can help me how to fix the code?
A simplified code snippet that demonstrates the problem here:
#include "stdafx.h"
#include <functional>
#include <memory>
class FS
{
};
typedef std::shared_ptr<FS> FSPtr;
class FSM
{
public:
FSM() : m_pFs(new FS()) {}
template <typename CALLABLE, typename... ARGS>
typename std::enable_if<std::is_same<bool, std::result_of_t<CALLABLE(ARGS&&...)>>::value, std::result_of_t<CALLABLE(ARGS&&...)>>::type
All(CALLABLE fn, ARGS&&... args) const // line 21
{
std::function<bool()> rFunc = std::bind(fn, m_pFs, args...);
bool bSuccess = rFunc();
return bSuccess;
}
private:
FSPtr m_pFs;
};
class SFF
{
public:
SFF() : m_pFsm(new FSM()) {}
bool VF(FSPtr pFs)
{
return nullptr != pFs;
}
bool Do()
{
return m_pFsm->All(std::bind(&SFF::VF, this, std::placeholders::_1)); // line 41
}
bool TF(FSPtr pFs, int n)
{
return nullptr != pFs && 0 != n;
}
bool Do1(int n)
{
return m_pFsm->All(std::bind(&SFF::TF, this, std::placeholders::_1, std::placeholders::_2), n); // line 49
}
private:
std::shared_ptr<FSM> m_pFsm;
};
int _tmain(int argc, _TCHAR* argv[])
{
SFF oSff;
bool bOk1 = oSff.Do();
bool bOk2 = oSff.Do1(4);
int rc = (bOk1 && bOk2) ? 0 : 1;
return rc;
}
And the errors VS2017 VC compiler output is:
1>------ Build started: Project: ConsoleApplication1, Configuration: Debug Win32 ------
1>ConsoleApplication1.cpp
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.11.25503\include\utility(486): error C2338: tuple index out of bounds
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.11.25503\include\functional(887): note: see reference to class template instantiation 'std::tuple_element<0,std::tuple<>>' being compiled
1>c:\program files (x86)\microsoft visual studio\2017\professional\vc\tools\msvc\14.11.25503\include\tuple(793): note: see reference to function template instantiation 'const tuple_element<_Index,_Tuple>::type &&std::get(const std::tuple<_Rest...> &&) noexcept' being compiled
1> with
1> [
1> _Tuple=std::tuple<_Rest...>
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: see reference to class template instantiation 'std::result_of<std::_Binder<std::_Unforced,bool (__thiscall SFF::* )(FSPtr),SFF *,const std::_Ph<1> &> (void)>' being compiled
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(21): note: while compiling class template member function 'std::enable_if<std::is_same<bool,result_of<_Ty>::type>::value,result_of<_Ty>::type>::type FSM::All(CALLABLE,ARGS &&...) const'
1> with
1> [
1> _Ty=CALLABLE (ARGS &&...)
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): error C2672: 'FSM::All': no matching overloaded function found
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): error C2893: Failed to specialize function template 'std::enable_if<std::is_same<bool,result_of<_Ty>::type>::value,result_of<_Ty>::type>::type FSM::All(CALLABLE,ARGS &&...) const'
1> with
1> [
1> _Ty=CALLABLE (ARGS &&...)
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: With the following template arguments:
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: 'CALLABLE=std::_Binder<std::_Unforced,bool (__thiscall SFF::* )(FSPtr),SFF *,const std::_Ph<1> &>'
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(41): note: 'ARGS={}'
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): error C2672: 'FSM::All': no matching overloaded function found
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): error C2893: Failed to specialize function template 'std::enable_if<std::is_same<bool,result_of<_Ty>::type>::value,result_of<_Ty>::type>::type FSM::All(CALLABLE,ARGS &&...) const'
1> with
1> [
1> _Ty=CALLABLE (ARGS &&...)
1> ]
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): note: With the following template arguments:
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): note: 'CALLABLE=std::_Binder<std::_Unforced,bool (__thiscall SFF::* )(FSPtr,int),SFF *,const std::_Ph<1> &,const std::_Ph<2> &>'
1>c:\users\s.chan\source\repos\consoleapplication1\consoleapplication1\consoleapplication1.cpp(49): note: 'ARGS={int &}'
1>Done building project "ConsoleApplication1.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Any help is much appreciated.
Apparently fn (CALLABLE) in FSM::All() is supposed to be called as fn(m_pFs, args...), not fn(args...)
So your SFINAE is wrong:
std::result_of_t<CALLABLE(ARGS&&...)> is missing the m_pFs argument:
std::result_of_t<CALLABLE(FSPtr, ARGS&&...)>
If you add FSPtr it should work. But keep in mind that result_of is deprecated. You can achieve the same effect simply with a trailing return type:
template <typename CALLABLE, typename... ARGS>
auto All(CALLABLE fn, ARGS&&... args)
-> std::enable_if_t<std::is_same_v<bool, decltype(fn(std::declval<FSPtr>(), args...))>, bool>
{
Note also that std::bind returns a lambda. Creating an std::function from that will be inefficient. Better to just use the returned type as-is:
auto rFunc = std::bind(fn, m_pFs, args...); // no need to cast to std::function
rFunc();
The reason why your code doesn't compile is the following:
bool Do()
{
return m_pFsm->All(std::bind(&SFF::VF, this, std::placeholders::_1));
}
Here you use wrapper function All to call VF function. But:
1) All function is supposed to get the function arguments:
template <typename CALLABLE, typename... ARGS>
typename std::enable_if<std::is_same<bool, std::result_of_t<CALLABLE(ARGS&&...)>>::value, std::result_of_t<CALLABLE(ARGS&&...)>>::type
All(CALLABLE fn, ARGS&&... args) const // line 21
{
std::function<bool()> rFunc = std::bind(fn, m_pFs, args...);
bool bSuccess = rFunc();
return bSuccess;
}
Args here should stand for single argument of FSPtr type, see the signature of SFF::VF.
So the correct code should be:
return m_pFsm->All(std::bind(&SFF::VF, this, std::placeholders::_1), somethingOfFSPtrType);
Related
I learned a bit about variadic templates and searched over the Internet for some samples and now trying to write some tricky code to call member a method of a variadic class template with one of its fields. I can't understand why it doesn't work. Please, help.
Here is sample classes:
class BarBase
{
public:
BarBase() = default;
virtual void call() = 0;
};
template<typename O, typename M, typename... A>
class Bar
: public BarBase
{
public:
Bar(O* o, M m, A&&... a)
: BarBase()
, m_o(o), m_m(m), m_a(std::forward<A>(a)...)
{ }
void call() override final
{
callInnerWithArgsInside();
}
private:
void callInnerWithArgsInside()
{
(m_o->*m_m)(m_a); // Some errors happends here
}
O* m_o;
M m_m;
std::tuple<typename std::remove_reference<A>::type...> m_a;
};
template<typename O, typename M, typename... A>
BarBase* crateBar(O* o, M m, A&&... a)
{
return new Bar<O, M, A...>(o, m, std::forward<A>(a)...);
}
And call from main:
struct Foo
{
void foo(int ii, float ff, std::string ss)
{
std::cout << "called" << std::endl;
}
};
int main()
{
Foo f;
int i = 10;
float ff = 20.2f;
std::string s = "Hello";
BarBase* bar = crateBar(&f, &Foo::foo, i, ff, s);
bar->call();
}
Errors:
main.cpp
1>d:\drafts_tests\main.cpp(203): error C2198: 'void (__thiscall Foo::* )(int,float,std::string)' : too few arguments for call
1> d:\drafts_tests\main.cpp(202) : while compiling class template member function 'void Bar::callInnerWithArgsInside(void)'
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
1> d:\drafts_tests\main.cpp(197) : see reference to function template instantiation 'void Bar::callInnerWithArgsInside(void)' being compiled
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
1> d:\drafts_tests\main.cpp(214) : see reference to class template instantiation 'Bar' being compiled
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
1> d:\drafts_tests\main.cpp(225) : see reference to function template instantiation 'BarBase *crateBar(O *,M,int &,float &,std::string &)' being compiled
1> with
1> [
1> O=Foo
1> , M=void (__thiscall Foo::* )(int,float,std::string)
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
you are passing a tuple to the function, rather than the individual type arguments. The following will pass the required type args to the call:
template<std::size_t... I>
void callInnerWithArgsInside2(std::index_sequence<I...>)
{
(m_o->*m_m)(std::get<I>(m_a)...);
}
void callInnerWithArgsInside()
{
return callInnerWithArgsInside2( std::make_index_sequence<sizeof...(A)>());
}
live demo
EDIT1: C++11 version
I have implemented a C++11 version, see updated live demo
I want to make a template function to queue both general functions and also class member functions. With internet search, I came up with the following code but have error in compiling it.
#include <iostream>
#include <functional>
#include <queue>
#include <string>
double MyFunction(double num1, double num2)
{
std::cout << "MyFunction( " << num1 << ", " << num2 << " )\n";
return num1 + num2;
}
class MyClass
{
public:
double MyClassFunction(double num1, double num2, double num3) const
{
std::cout << "MyClass::MyClassFunction( " << num1 << ", " << num2 << ", " << num3 << " )\n";
return num1 + num2 + num3;
}
};
struct MyFunctionQueue
{
template <typename FUNC, typename... ARGS>
void QueueFunction(FUNC fn, ARGS&&... args)
{
std::function<std::result_of_t<FUNC(ARGS...)> () > rFunc = std::bind(fn, args...);
_myFQ.push(rFunc);
}
void Execute()
{
while (!_myFQ.empty())
{
_myR.push(_myFQ.front()());
_myFQ.pop();
}
}
double PopResult()
{
double r = _myR.front();
_myR.pop();
return r;
}
std::queue<std::function<double()>> _myFQ;
std::queue<double> _myR;
};
int main()
{
MyFunctionQueue funcQue;
funcQue.QueueFunction(MyFunction, 1.234, 2.345);
MyClass obj;
funcQue.QueueFunction(&MyClass::MyClassFunction, std::ref(obj), 1.234, 2.345, 3.456);
funcQue.Execute();
std::cout << "MyFunction result: " << funcQue.PopResult() << std::endl;
std::cout << "MyClass::MyClassFunction result: " << funcQue.PopResult() << std::endl;
}
I know the error is with the template code generation on the class member function. With my limited knowledge in using templates I cannot figure out what's wrong with it. Can anyone help to point out the mistake in the code? And the error I've got from VC++ is:
1>------ Build started: Project: TestFunctionQueue, Configuration: Debug Win32 ------
1> TestFunctionQueue.cpp
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1469): error C2672: 'std::invoke': no matching overloaded function found
1> c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(31): note: see reference to class template instantiation 'std::result_of<FUNC (std::reference_wrapper<MyClass>,double,double,double)>' being compiled
1> with
1> [
1> FUNC=double (__thiscall MyClass::* )(double,double,double) const
1> ]
1> c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(63): note: see reference to function template instantiation 'void MyFunctionQueue::QueueFunction<double(__thiscall MyClass::* )(double,double,double) const,std::reference_wrapper<MyClass>,double,double,double>(FUNC,std::reference_wrapper<MyClass> &&,double &&,double &&,double &&)' being compiled
1> with
1> [
1> FUNC=double (__thiscall MyClass::* )(double,double,double) const
1> ]
1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1469): 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(1469): note: With the following template arguments:
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1469): note: '_Callable=double (__thiscall MyClass::* )(double,double,double) const'
1> c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1469): note: '_Types={std::reference_wrapper<MyClass>, double, double, double}'
1>c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(31): error C2440: 'initializing': cannot convert from 'std::_Binder<std::_Unforced,FUNC &,std::reference_wrapper<MyClass> &,double &,double &,double &>' to 'std::function<unknown-type (void)>'
1> with
1> [
1> FUNC=double (__thiscall MyClass::* )(double,double,double) const
1> ]
1> c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(31): note: No constructor could take the source type, or constructor overload resolution was ambiguous
1>c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(32): error C2664: 'void std::queue<std::function<double (void)>,std::deque<_Ty,std::allocator<_Ty>>>::push(const std::function<double (void)> &)': cannot convert argument 1 from 'std::function<unknown-type (void)>' to 'std::function<double (void)> &&'
1> with
1> [
1> _Ty=std::function<double (void)>
1> ]
1> c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(32): note: Reason: cannot convert from 'std::function<unknown-type (void)>' to 'std::function<double (void)>'
1> c:\users\siu.chan\documents\visual studio 2015\projects\testfunctionqueue\testfunctionqueue\testfunctionqueue.cpp(32): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
So, I think this is an issue with vc++. Passing a member function works just fine when the next parameter is a pointer, but fails when it is a reference.
template <class F, class... A>
std::result_of_t<F(A...)> Foo::bar(F f, A... a)
{
return 0;
}
Foo f;
f.bar(&Foo::other_function, std::ref(f)); //error
f.bar(&Foo::other_function, &f); //success
Additionally, result_of in vc++ fails when the function type is a function (like void()), but clang and gcc both succeed.
template <class F, class... A>
struct Foo
{
typedef std::result_of_t<F(A...)> type;
};
Foo<int(int), int> //fails in only vc++
clang 3.4 accepts the following code; while vc++ NOV 2013 CTP rejects it with an error:
error C2668: 'AreEqual' : ambiguous call to overloaded function
template<class headT, class... tailTypes>
constexpr headT&& __GetFirst__(headT&& value, tailTypes&&...)
{
return static_cast<headT&&>(value);
};
template<class T>
constexpr bool AreEqual(const T& a, const T& b)
{
return a == b;
}
template<class headT, class... tailTypes>
constexpr bool AreEqual(const headT& head_value, const tailTypes&... tail_values)
{
return AreEqual(head_value, __GetFirst__(tail_values...))
&& AreEqual(tail_values...);
}
int main()
{
AreEqual(1, 1, 2, 1);
}
Which compiler is correct as per the C++14 standard?
Update: The full error message:
error C2668: 'AreEqual' : ambiguous call to overloaded function
1> d:\projects\ktl\test\main.cpp(20): could be 'bool AreEqual<headT,int>(const headT &,const int &)'
1> with
1> [
1> headT=int
1> ]
1> d:\projects\ktl\test\main.cpp(8): or 'bool AreEqual<headT>(const T &,const T &)'
1> with
1> [
1> headT=int
1> , T=int
1> ]
1> while trying to match the argument list '(const int, const int)'
1>
1>Build FAILED.
Clang (and GCC)'s behavior is correct. You can read ยง14.8.2.4 [temp.deduct.partial] of the standard for how the partial ordering for function templates is done, but the example given in p8 of that subclause directly covers this situation:
template<class... Args> void f(Args... args); // #1
template<class T1, class... Args> void f(T1 a1, Args... args); // #2
template<class T1, class T2> void f(T1 a1, T2 a2); // #3
f(); // calls #1
f(1, 2, 3); // calls #2
f(1, 2); // calls #3; non-variadic template #3 is more
// specialized than the variadic templates #1 and #2
I'm trying to make a manager, which provides access via templates to containers that store different types of data.
First of all, i made base manager class for storing different type of data
template <typename T>
class BaseMapManager{
public:
T add(T element, std::string name);
T remove(T element);
T remove(std::string name);
T remove(unsigned int id);
T get(std::string name);
T get(unsigned int id);
BaseMapManager() { id = 0; };
~BaseMapManager() { nameMap.clear(); idMap.clear(); };
protected:
std::map<std::string, T> nameMap;
std::map<unsigned int, T> idMap;
//each element of type T gets new unique id
unsigned int id;
//hide it
BaseMapManager(const BaseMapManager&) {};
BaseMapManager& operator=(const BaseMapManager&) {};
};
Then i made my concrete manager, which had to store 3 types of baseMapManagers:
class ResourceManager{
public:
//creates singleton
static ResourceManager* init(){
static ResourceManager singleton;
return &singleton;
}
template <typename T>
void load_resource(std::string path, std::string name){
get_map<std::shared_ptr<T>>().add(std::shared_ptr<T>(new T(path, name), name);
}
template <typename T>
std::shared_ptr<T> get_resource(std::string name){
get_map<T>().get(name);
}
template <typename T>
std::shared_ptr<T> get_resource(unsigned int id){
get_map<T>().get(id);
}
private:
BaseMapManager<std::shared_ptr<AnimationResource> > animationMap;
BaseMapManager<std::shared_ptr<ImageResource> > imageMap;
BaseMapManager<std::shared_ptr<FontResource> > fontMap;
template <typename T>
BaseMapManager<T>& get_map(){
if (std::is_same<T, std::shared_ptr<AnimationResource> >() == true) return animationMap;
if (std::is_same<T, std::shared_ptr<ImageResource> >() == true) return imageMap;
if (std::is_same<T, std::shared_ptr<FontResource> >() == true) return fontMap;
};
};
and now i got this:
1>------ Build started: Project: BOSS, Configuration: Debug Win32 ------
1> main.cpp
1>d:\programming\github projects\boss\boss\new\resourcemanager\resourcemanager.h(43):
error C2440: 'return' : cannot convert from 'BaseMapManager<T>' to 'BaseMapManager<T> &'
1> with
1> [
1> T=std::shared_ptr<ImageResource>
1> ]
1> and
1> [
1> T=std::shared_ptr<AnimationResource>
1> ]
1> d:\programming\github projects\boss\boss\new\resourcemanager\resourcemanager.h(21) : see reference to function template instantiation 'BaseMapManager<T> &ResourceManager::get_map<std::shared_ptr<_Ty>>(void)' being compiled
1> with
1> [
1> T=std::shared_ptr<AnimationResource>,
1> _Ty=AnimationResource
1> ]
1> d:\programming\github projects\boss\boss\new\main.cpp(17) : see reference to function template instantiation 'void ResourceManager::load_resource<AnimationResource>(std::string,std::string)' being compiled
1>d:\programming\github projects\boss\boss\new\resourcemanager\resourcemanager.h(44): error C2440: 'return' : cannot convert from 'BaseMapManager<T>' to 'BaseMapManager<T> &'
1> with
1> [
1> T=std::shared_ptr<FontResource>
1> ]
1> and
1> [
1> T=std::shared_ptr<AnimationResource>
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The goal is - providing access to different types on data using template functions like
manager->get_resource<AnimationResource>(itsName) //get it or
manager->load_resource<FontResource>(itsPath, itsName) //add it
Is there a better way to handle this maybe? Thanks!
edit
The source of your problem seams to be the is_same function not resolving correctly at compile time.
/edit
You can always have template functions that only work with the specified types.
For example:
template<>
BaseMapManager<AnimationResource> get_map<AnimationResource>()
{
return animationMap;
}
template<>
BaseMapManager<ImageResource> get_map<ImageResource>()
{
return imageMap;
}
template<>
BaseMapManager<FontResource> get_map<FontResource>()
{
return fontMap;
}
I am trying following code from book C++ Template The Complete Guide , but it is failing to compile with Visual Studio 2010.
template < typename T>
class IsClassType {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
template<typename C> static Two test(...);
public:
enum { Yes = sizeof ( IsClassType<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
template < typename T>
void check() {
if ( IsClassType<T>::Yes ) {
std::cout << " IsClassType Yes " << std::endl;
} else {
std::cout << " IsClassType No " << std::endl;
}
}
int main() {
std::cout << "Int: ";
check<int>();
}
Following is the compiler error.
1>------ Build started: Project: test123, Configuration: Debug Win32 ------
1> test123.cpp
1>e:\svn\office_expt\test123\test123\test123.cpp(45): error C2783: 'IsClassType<T>::Two IsClassType<T>::test(...)' : could not deduce template argument for 'C'
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(42) : see declaration of 'IsClassType<T>::test'
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(51) : see reference to class template instantiation 'IsClassType<T>' being compiled
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(60) : see reference to function template instantiation 'void check<int>(void)' being compiled
1>e:\svn\office_expt\test123\test123\test123.cpp(45): error C2784: 'IsClassType<T>::One IsClassType<T>::test(int C::* )' : could not deduce template argument for 'int C::* ' from 'int'
1> with
1> [
1> T=int
1> ]
1> e:\svn\office_expt\test123\test123\test123.cpp(41) : see declaration of 'IsClassType<T>::test'
1> with
1> [
1> T=int
1> ]
1>e:\svn\office_expt\test123\test123\test123.cpp(45): error C2056: illegal expression
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Is it this line:
template<typename C> static Two test(...);
The typename cannot be inferred from this. I think it should read:
static Two test(...);