Compile error with template specialization, default arguments and VS2013 - c++

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);
}

Related

Compiler warning from std::chrono but is not being used

NOTE: This bug only occurs with x64 projects in both release and debug modes.
Odd warnings involving std::chrono appear on this piece of code using VC2019 at warning level 3. This is a stripped down piece of code that processes command line flags. I've removed most of the guts that aren't relevant to the problem.
#if 1 // enable bug
#include <chrono> // excluding this also eliminates chrono warnings
using CorrectedIntType=int;
#else
using CorrectedIntType=size_t;
#endif
#include <iostream>
#include <vector>
#include <string>
#include <type_traits>
using std::vector;
using std::string;
namespace {
void fixup(const std::string& argcmd, std::string& arg) { arg = argcmd; }
template<class T>
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg)
{
fixup(arglist[idx], arg);
arglist.erase(arglist.begin() + idx);
}
template<class T, class ...TA>
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg, TA&...argv)
{
procVal(arglist, idx, arg);
procVal(arglist, idx, argv...);
}
template<class T, class ...TA>
bool procFlag(const char* pc, std::vector<std::string>& arglist, T& arg1, TA&...argv)
{
std::string flag(pc);
for (size_t i = 0; i < arglist.size(); i++)
{
if (arglist[i] == flag)
{
arglist.erase(arglist.begin() + i);
procVal(arglist, i, arg1); // process one argument after flag
return true;
}
}
return false;
}
}
int main()
{
string outfile;
vector<string> test = { "test" };
procFlag("-o", test, outfile); // assigns test[0] to outfile and removes it
std::cout << outfile << '\n';
}
Warnings:
1>Source.cpp
1>C:\Users\mgray\Documents\Visual Studio 2017\Projects\CommandLineCPP\stackoverflow\Source.cpp(35,1): warning C4267: 'argument': conversion from 'size_t' to 'CorrectedIntType', possible loss of data
1>C:\Users\mgray\Documents\Visual Studio 2017\Projects\CommandLineCPP\stackoverflow\Source.cpp(54): message : see reference to function template instantiation 'bool `anonymous-namespace'::procFlag<std::string,>(const char *,std::vector<std::string,std::allocator<std::string>> &,T &)' being compiled
1> with
1> [
1> T=std::string
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(632): message : see reference to class template instantiation 'std::chrono::duration<double,std::ratio<1,1>>' being compiled
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(178): message : see reference to class template instantiation 'std::chrono::duration<__int64,std::nano>' being compiled
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.24.28314\include\chrono(610): message : see reference to class template instantiation 'std::chrono::time_point<std::chrono::steady_clock,std::chrono::nanoseconds>' being compiled
While the code works, even with the int -<> size_t conversion issue which is a legitimate warning, all warnings go away when the macro at the top is set to 0. So somehow the size difference between size_t and int triggers chrono messages. It concerns me that the chrono warnings exist since it isn't involved. Is this a bug in VS2019? Any ideas as to why the chrono warning references are occurring?
This is a valid warning and it is not related to <chrono> but to your own code and CorrectedIntType type. Here is a simplified code without <chrono>: https://gcc.godbolt.org/z/qf9v8TEh7
In the definition of procVal, the second parameter is CorrectedIntType:
void procVal(std::vector<std::string>& arglist, CorrectedIntType idx, T& arg)
but it is called from procFlag with size_t value:
bool procFlag(const char* pc, std::vector<std::string>& arglist, T& arg1)
...
for (size_t i = 0; i < arglist.size(); i++)
...
procVal(arglist, i, arg1);
So one can fix the warning by changing i type to CorrectedIntType as well.

VS2015 with VC++ 2008 toolset template deduction failure

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

Smart pointer r-value ref conversion operator

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 function type deduction and operator<<

When I compile the following code with MSVC++, I get an error:
struct A
{
template<typename T>
void operator<<(T&& x)
{
}
};
void f()
{
}
int main()
{
A().operator<<( f ); // ok
A() << f; // error
return 0;
}
g++ and clang both compile this code fine.
AFAIK, 'ok' and 'error' lines do exactly the same thing, and type T is deduced to void(&)(). Or is it void() and rvalue references to function are allowed? If so, what is their meaning?
Is it ok to pass functions by reference like that? Is it MSVC++ bug that it fails to compile 'error' line? BTW, the error output:
no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion)
could be 'void A::operator <<<void(void)>(T (__cdecl &&))'
with[ T=void (void) ]
Why void operator<<(T&& x)? void operator<<(T& x) serves the purpose.
Function can be called with x() inside overloaded function as below
struct A
{
template<typename T>
void operator<<(T& x)
{
x();
}
};
void f()
{
}
int main()
{
A().operator<<( f );
A() << f;
return 0;
}
So, answering my own question:
The code provided is valid and while rvalue references to functions are allowed (they act identical to lvalue references), here during template deduction T should become void(&)().
A bug in MSVC prevents my code from compiling.
UPDATE: The bug has been fixed in Visual Studio 2013 compiler

Code compiling on gcc, but not on msvc

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.