Sample code to reproduce the problem:
#include <iostream>
template< typename T, typename Func >
void action(Func T::* func)
{
T entry;
(entry.*func)();
}
struct A
{
void f()
{
std::cout << "A::f()" << std::endl;
}
};
int main()
{
action(&A::f);
return 0;
}
This code successfully compiles using MS VC++2008, with VC++2015 using vc140 toolset, but fails to compile when used in VC++2015 project with vc90 (VC++2008) toolset. Gives strange diagnostics of
cpptest.cpp(20): error C2664: 'action' : cannot convert parameter 1 from 'void (__thiscall A::* )(void)' to 'void (A::* )(void)'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Seems compiler looses __thiscall calling convention specifier when deducing type for Func. Tried to forcibly specify __thiscall in different parts of the code but had no success. Converting the whole project to vc14 toolset is not a way because of various dependencies and keeping it under the VS 2008 is an unlikely way. Any ideas to force compiler to understand such a construction?
Update
Changing code to
template< typename T, typename Func >
void action(Func func)
....
and calling to action< A >( &A::f ); works, but looks little ugly - I expect compiler to be able to deduce type for both template arguments (T and Func) automatically
Related
I am trying to write a function that takes an Eigen::Vector<T, dim> as a parameter. However, the following example fails to compile:
#include <Eigen/Core>
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Vector<T, dim>& arg1) {
}
template<typename T, int dim>
void foo(const Eigen::Vector<T, dim>& a) {
return bar([] {}, a);
}
int main() {
Eigen::Vector<float, 3> v1{ 1.f,2.f,3.f };
foo(v1);
return 0;
}
This, under Visual Studio 2019, gives me the following error:
1>main.cpp(9,10): error C2672: 'bar': no matching overloaded function found
1>main.cpp(14): message : see reference to function template instantiation 'void foo<float,3>(const Eigen::Matrix<float,3,1,0,3,1> &)' being compiled
1>main.cpp(9,1): error C2784: 'void bar(F &&,const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &)': could not deduce template argument for 'const Eigen::Matrix<T,dim,1,|_Rows==&&?:&&_Rows!=?:,_Rows,1> &' from 'const Eigen::Matrix<float,3,1,0,3,1>'
1>main.cpp(4): message : see declaration of 'bar'
My questions:
What is this weird |_Rows==&&?:&&_Rows!=?: in the error message?
What can I do to make the above code compile?
The bar function should have T and dim availabe. I cannot just take const AnyType& arg1, because the actual implementation of bar depends on compile-time known values T and dim.
I have seen https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html. I think I understand what they are saying, but I am not sure if it applies here. I am taking an actual Eigen::Vector as an argument, not an expression.
If there was an expression it would be fine for me, to have it materialized.
Nevertheless, if I try to follow their instruction and just use ArrayBase<Derived>, I lose the compile-time information about T and dim.
This indeed looks like an MSVC issue, it compiles fine with gcc >= 4.7, and clang >= 3.5: https://godbolt.org/z/kqoHyO
One possible workaround would be to explicitly write out what Eigen::Vector expands to:
template<class F, typename T, int dim>
void bar(F&& func, const Eigen::Matrix<T, dim, 1, 0, dim, 1>& arg1) {
}
https://godbolt.org/z/vlvSDP
The weird |_Rows==&&?:&&_Rows!=?: looks like MSVC mangled the default value of the Options template parameter:
AutoAlign |
( (_Rows==1 && _Cols!=1) ? Eigen::RowMajor
: (_Cols==1 && _Rows!=1) ? Eigen::ColMajor
: EIGEN_DEFAULT_MATRIX_STORAGE_ORDER_OPTION ),
If you want to get to the bottom of this, you should file a bug-report to the MSVC maintainers, maybe using a simplified example like this: https://godbolt.org/z/U_0Sh7 (probably it's possible to reduce this even more).
I'm trying to write a smart pointer that can easily upcast, but I'm running into trouble with upcasting r-value references. Consider the following:
#include <utility>
template<typename T>
class SmartPtr
{
T* impl_;
public:
// ...
template<typename U>
operator SmartPtr<U>&&() &&
{
U* u = impl_; // Fail with a nice error message if T* isn't implicitly convertable to U*;
return reinterpret_cast<SmartPtr<U>&&>(*this);
}
// ...
};
struct A {};
struct B : public A {};
int main()
{
SmartPtr<B> pb;
SmartPtr<A>&& pa = std::move(pb);
}
This fails in Visual C++ 2015 with the following error message:
error C2440: 'initializing': cannot convert from 'SmartPtr<B>' to 'SmartPtr<A> &&'
note: Reason: cannot convert from 'SmartPtr<B>' to 'SmartPtr<A>'
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Note: this works in Clang 3.4 and simpler versions (e.g. a wrapper class being implicitly convertible to an r-value-ref of the inner value) work in Visual Studio 2015.
Should what I'm trying to do work according to the standard? Is this a bug in Visual Studio 2015?
template<typename T>
void f(const T &v = T());
template<>
void f<std::string>(const std::string &v)
{
std::cout << v;
}
int main(int argc, char* argv[])
{
f<std::string>(); // Error in VS2013, OK in VS2012, gcc-4.7
f<std::string>("Test"); // OK
f<std::string>(std::string()); //OK
return 0;
}
The latest Visual Studio 2013 compiler gives the following compiler error for the case when the default argument must be used:
error C2440: 'default argument' : cannot convert from 'const std::string *' to 'const std::string &'
Reason: cannot convert from 'const std::string *' to 'const std::string'
No constructor could take the source type, or constructor overload resolution was ambiguous
Visual Studio 2012 and gcc-4.7 compile fine.
Update: As it seems to be a VS2013 bug, are there any temporary workarounds that do not require significant code changes until this is fixed by MS? Bug report was submitted on MS connect.
Whenever I see this kind of problems with template functions, I try to switch to template structures (if you need a temporary workaround)...
template<typename T>
struct foo
{
static void f(const T &v = T());
};
template<>
struct foo<std::string>
{
static void f(const std::string &v = std::string())
{
std::cout << v;
}
};
Unfortunately, I can't check this in Visual Studio 2013 because I don't have it, but I hope it should work.
The downside here is that you should explicitly specify your type, it's no longer deducted
foo<std::string>::f()
foo<std::string>::f("Text")
My wild guess here would be adding like a wrapper function:
template<typename T>
void f_wrapper(const T &v = T())
{
foo<T>::f(v);
}
I have a problem compiling a template using msvc-2010. It works perfectly using gcc 4.6.3.
I have boiled down the code to the essential (it doesn't make sense of course):
//Variant that works
template <typename T, T* Ptr>
void callFun()
{
}
//Traits class (type expands to the same type T* as above)
template <typename T>
class TraitsClass
{
public:
typedef T* type;
};
//Essentially the same as callFun2, only that the
//type of Ptr is expressed indirectly over a traits class
//The usage of this class is not possible, because of the error described below
template <typename T, typename TraitsClass<T>::type Ptr>
void callFun2()
{
}
//Provides a compile constant ptr for this example
void testFun()
{
}
int main()
{
//Works
callFun<void(), &testFun>();
//Fails
callFun2<void(), &testFun>();
//Works
callFun2<void(), 0>();
return 0;
}
The Error:
error C2975: 'Ptr' : invalid template argument for 'callFun2', expected compile-time constant expression
I find it interesting, that it only fails when the second type parameter is being used through a typedef in a Traits class.
g++ compiles this example correctly without warnings, even when using -Wall -Wextra -Werror -pedantic (Except for the unused parameters, of course)
Thank you very much.
Well, I think that the answer is that compilers are not written by gods. Programming standards in the compiler industry are extremely high, MS C++ is a good compiler, but it still contain bugs. I came across the following, that is somehow similar to what you are pointing at:
template <class item_struct>
struct THeapBasedArray
{
void Sort(int (__cdecl *compareFunction)(const item_struct *item1,
const item_struct *item2));
};
struct Item { int x; };
struct ItemPtrsArray : public THeapBasedArray<Item*>
{
static int __cdecl Compare1(const Item **pp1, const Item **pp2);
typedef Item *ItemPtr;
static int __cdecl Compare2(const ItemPtr *pp1, const ItemPtr *pp2);
};
int main()
{
ItemPtrsArray vect;
vect.Sort(ItemPtrsArray::Compare1);
vect.Sort(ItemPtrsArray::Compare2);
}
The first call to Sort fails with:
cpptest1.cxx(21) : error C2664: 'THeapBasedArray::Sort' : cannot convert parameter 1 from 'int (_cdecl *)(const Item **, const Item **)' to 'int (_cdecl *)(const item_struct *, const item_struct *)
while the second call compilers fine. For me this is a bug in a compiler. Sometimes this happens. I guess this is the answer.
I've heard a little bit about reference-to-reference problem and this resolution. I'm not very good with C++ Committee terminology, but I understand the "Moved to DR" annotation in the link means that this is the current interpretation that standard-conforming compilers should adhere to.
I have this sample code that I can't understand:
template <typename T>
struct C {
void f(T&) { }
void f(const T&) { }
};
int main() {
C<int> x; // OK
C<int&> y; // compile error: f cannot be overloaded
C<const int&> z; // compile error: f cannot be overloaded
}
I understand the error in C<const int&> case: using rules from DR #106 we get two methods with the same signature f(const int&). What I don't get is the C<int&> case: shouldn't it generate exactly the same code as C<int> (at least according to Stroustrup's resolution)?
DR only means "Defect Report", and to my knowledge, the described resolution hasn't made it (yet) to the standard. For this reason, I believe a strictly conforming C++03 implementation should not compile this code because of it is forming a reference to a reference.
[Edit] Just found a nice answer on this issue.
Interestingly, when I compile your code (Visual C++ 10 Express) I get errors, but also when I try this simpler case:
int main(int argc, char* argv[])
{
C<int> x; // OK
C<const int> x1; // error C2535: 'void C<T>::f(T &)' : member function
// already defined or declared
return 0;
}
Seems like the ref-to-ref collapsing defined in the DR you mentioned means that const ref becomes a simple non-const ref within the template. My problem with this is that I don't understand why the second f is not just ignored.
If I change C so that the second f is const-qualified, this now compiles:
template <typename T>
struct C {
void f(T&) { }
void f(const T& t) const {}
};
The implication seems to be that when C is instantiated with const anything (ref or not), the two C::f overloads are simply identical, and result in compile-time duplicate detection.
Perhaps somebody smarter than me can decipher the chain more definitively here.
EDIT: On reflection, it's not surprising here that T = const int& results in the f overloads being identically instantiated as
void f(const int&) {}
That's what the compiler is telling me:
#include "stdafx.h"
template <typename T>
struct C {
void f(T&) { }
void f(const T&) { }
};
int main() {
C<const int&> z; // compile error: f cannot be overloaded
return 0;
}
gives this error:
1>test.cpp(6): error C2535: 'void C<T>::f(T)' : member function already
defined or declared
1> with
1> [
1> T=const int &
1> ]
1> test.cpp(5) : see declaration of 'C<T>::f'
1> with
1> [
1> T=const int &
1> ]
1> test.cpp(10) : see reference to class template instantiation
'C<T>' being compiled
1> with
1> [
1> T=const int &
1> ]
I'm not even convinced this has anything to do with the DR.