I'm constructing a simple container class but run into some problems (reassembling the ones in Visual C++ 2010, rvalue reference bug?)
#include <cassert>
#include <utility>
template<typename T0>
class MyType {
public:
typedef T0 value_type;
// Default constructor
MyType() : m_value() {
}
// Element constructor
explicit MyType(const T0 &c_0) : m_value(c_0) {
}
template<typename S0>
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
}
// Copy constructor
MyType(const MyType &other) : m_value(other.m_value) {
}
MyType(MyType &&other) : m_value(std::forward<value_type>(other.m_value)) {
}
// Copy constructor (with convertion)
template<typename S0>
MyType(const MyType<S0> &other) : m_value(other.m_value) {
}
template<typename S0>
MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
}
// Assignment operators
MyType &operator=(const MyType &other) {
m_value = other.m_value;
return *this;
}
MyType &operator=(MyType &&other) {
m_value = std::move(other.m_value);
return *this;
}
template<typename S0>
MyType &operator=(const MyType<S0> &other) {
m_value = other.m_value;
return *this;
}
template<typename S0>
MyType &operator=(MyType<S0> &&other) {
m_value = std::move(other.m_value);
return *this;
}
// Value functions
value_type &value() {
return m_value;
}
const value_type &value() const {
return m_value;
}
private:
template<typename S0>
friend class MyType;
value_type m_value;
};
int main(int argc, char **argv) {
MyType<float> t1(5.5f);
MyType<double> t2(t1);
return 0;
}
The above code gives the following error:
1>ClCompile:
1> BehaviorIsolation.cpp
1>behaviorisolation.cpp(18): error C2440: 'initializing' : cannot convert from 'MyType<T0>' to 'double'
1> with
1> [
1> T0=float
1> ]
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1> behaviorisolation.cpp(78) : see reference to function template instantiation 'MyType<T0>::MyType<MyType<float>&>(S0)' being compiled
1> with
1> [
1> T0=double,
1> S0=MyType<float> &
1> ]
1>behaviorisolation.cpp(18): error C2439: 'MyType<T0>::m_value' : member could not be initialized
1> with
1> [
1> T0=double
1> ]
1> behaviorisolation.cpp(73) : see declaration of 'MyType<T0>::m_value'
1> with
1> [
1> T0=double
1> ]
1>
1>Build FAILED.
How can one remedy this error without using tricks like the ones described in the linked question?
Thanks!
Edit:
What confuses me the most is why isn't any of the two specialized constructors called. They fit the call a lot better.
// Copy constructor (with convertion)
template<typename S0>
MyType(const MyType<S0> &other) : m_value(other.m_value) {
}
template<typename S0>
MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
}
Your linked question already answers this. Let's define
typedef MyType<float> MF;
typedef MyType<double> MD;
When you say MD t2(t1);, you would like to call the constructor MF::MF(const MD &). However, the constructor template <typename T> MF::MF(T&&) matches better, because it takes T = MD& and thus resolves to MF::MF(MD&), which is a better match due to the absence of const.
To resolve this, you should essentially get rid of the MF(T&&) constructor, as Howard suggested already. Since you only intend that constructor for values anyway, my first suggestion would be to change the signature to MF(const T &), which would already solve your problem. Another solution would be to add a constructor with signature MF(MD&) (non-const). That's ugly, though. Finally, you could call the constructor explicity at the call site: MD t2(MF(t1)), or MD t2(std::forward<MF>(t1)) or even MD t2(std::move(t1)), if that's an option.
Finally note that if you are only dealing with primitive members, there's nothing to be gained from explicit moves, so you might as well not bother defining all those constructors separately.
Your constructor:
template<typename S0>
explicit MyType(S0 &&c_0)
is overly generic and the best way to solve your problem is to restrict the type which can be deduced as S0. This is basically what the linked answer does to. But perhaps I can make it prettier for you.
Here it is, in std::C++11:
template<typename S0,
class = typename std::enable_if
<
std::is_convertible<S0, T0>::value
>::type>
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
}
If that is just too ugly, you might consider:
#define restrict_to(x...) class = typename std::enable_if<x>::type
...
template<typename S0, restrict_to(std::is_convertible<S0, T0>::value)>
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
}
Note that this will not only solve your problem, but if your clients subsequently ask questions like:
std::is_convertible<X, MyType<T>>::type
they will now get the correct answer. As you currently have it coded, the above trait always answers true.
Related
While creating a subclass of a template class I noticed I got an error on an overloaded function.
This compiler error was correct since one of the overloads was using a copy and the type was not copyable.
However, I was not using that function (correct overload or not). So I was surprised to get this error
After searching around a bit and reproducing in godbolt the culprit seemed to be __declspec(dllexport).
Reproduction in godbolt
Removing the declspec seems to result in the correct compilation.
Code in godbolt:
#include <memory>
#include <vector>
using namespace std;
template<class V>
struct Foo{
void update(const V& v);
void update(V&& v);
std::vector<V> values;
};
template<class V>
void Foo<V>::update(const V& v)
{
values[0] = v;
}
template<class V>
void Foo<V>::update(V&& v)
{
values[0] = std::move(v);
}
struct __declspec(dllexport) Bar : public Foo<std::unique_ptr<int>>
{
};
int main()
{
Bar f;
auto i = std::make_unique<int>(5);
//f.update(i);
//f.update(std::move(i));
}
My questions are mostly, how is declspec causing this behavior?
And, is there anything that can be done about this in the template class or derived class?
Error log:
(19): error C2280:
'std::unique_ptr>
&std::unique_ptr>::operator =(const
std::unique_ptr> &)': attempting to
reference a deleted function with [
_Ty=int ] C:/data/msvc/14.16.27023.1/include\memory(2338): note: see declaration of
'std::unique_ptr>::operator =' with
[
_Ty=int ] C:/data/msvc/14.16.27023.1/include\memory(2338): note: 'std::unique_ptr>
&std::unique_ptr>::operator =(const
std::unique_ptr> &)': function was
explicitly deleted with [
_Ty=int ] (18): note: while compiling class template member function 'void
Foo>>::update(const V &)'
with [
_Ty=int,
V=std::unique_ptr> ] (29): note: see reference to class template instantiation
'Foo>>' being compiled
with [
_Ty=int ]
I'm trying to make a for_each function for a generic object that uses an size function and an item index function. But I'm having some difficulty with the syntax.
This is what I have so far (starting at line 128):
class base1
{
protected:
std::vector<int> items;
public:
base1()
: items({1,2,3})
{
}
int GetCount() const
{
}
};
class base2 : public base1
{
public:
base2()
: base1()
{
}
int GetItem(int i) const
{
return items[i];
}
};
class derived : public base2
{
public:
derived()
: base2()
{
}
};
template <typename CONTAINER, typename CONTAINER_BASE1, typename CONTAINER_BASE2, typename SIZE, typename CONTAINED, typename FUNC>
void for_each(CONTAINER* container, SIZE (CONTAINER_BASE1::*GetSize)() const, CONTAINED (CONTAINER_BASE2::*GetItem)(SIZE) const, FUNC& body)
{
for (SIZE i = 0; i < container->*GetSize(); ++i)
{
body(container->*GetItem(i));
}
}
void fn()
{
derived x;
for_each(&x, &derived::GetCount, &derived::GetItem, [](int i){
++i;
});
}
Right now, I get an error from VC++ 2013 stating:
1>d:\projects\test\test.cpp(169): error C2064: term does not evaluate to a function taking 0 arguments
1> d:\projects\test\test.cpp(180) : see reference to function template instantiation 'void for_each<derived,base1,base2,int,int,fn::<lambda_862ea397905775f7e094cde6fe9b462c>>(CONTAINER *,SIZE (__thiscall base1::* )(void) const,CONTAINED (__thiscall base2::* )(SIZE) const,FUNC &)' being compiled
1> with
1> [
1> CONTAINER=derived
1> , SIZE=int
1> , CONTAINED=int
1> , FUNC=fn::<lambda_862ea397905775f7e094cde6fe9b462c>
1> ]
1>d:\projects\test\test.cpp(171): error C2064: term does not evaluate to a function taking 1 arguments
Any ideas as to what the problem is?
You have two bugs. You take the functor by non-const lvalue reference - FUNC& body - which doesn't bind to a temporary like a lambda; this was hidden by a terrible MSVC extension that allows such bindings. You should accept the function object by value (the way it is usually done by the standard library), by const lvalue reference (if copying is expensive and/or identity is important), or by forwarding reference (if identity is important and operator() can be non-const).
Second is operator precedence. The postfix function call operator has higher precedence than .* and ->*. container->*GetSize() is container->*(GetSize()); you want (container->*GetSize)().
I'm also not sure about this design. It's probably better to provide a uniform interface, and simply do, e.g., container.size() and container.at(i) than using this tortured system of pointer-to-member-functions.
Now I'm migrating my project from Visual Studio 2008 to 2013 (with no updates installed), and facing a problem.
I have a sort of Variant type, CData< T>, that has a conversion operators to the contained type T.
template<class T> T& GetValue();
template<class T> const T& GetValue() const;
template<class T> T* GetValuePtr();
template<class T> const T* GetValuePtr() const;
template<class T> operator T&() { return GetValue<T>(); }
template<class T> operator const T&() const { return GetValue<T>(); }
template<class T> operator T*() { return GetValuePtr<T>(); }
template<class T> operator const T*() const { return GetValuePtr<T>(); }
There are methods in a class CDataArray:
CData& Get(int Index) { return At(Index); }
const CData& Get(int Index) const { return operator [](Index); }
template<class T>
T& Get(int Index) { return At(Index).GetValue<T>(); }
template<class T>
const T& Get(int Index) const { return operator [](Index).GetValue<T>(); }
and there is a call to it:
Data::PDataArray SGQuests;
Data::PParams SGQuest = SGQuests->Get(i);
Here, Data::PParams is going to be T.
In VS2008, it seems it used non-const non-template CDataArray::Get(), which returned CData&, and then this CData& was accessed with non-const template operator T&(), finally returning Data::PParams&.
In VS2013, it for some reason uses a const overload and it results in error:
1>PATH_TO_SRC\l1\data\type.h(126): error C2678: binary "=": no operator found that accepts left operand "const Ptr<Data::CParams>" (or there is no acceptable conversion)
1> PATH_TO_SRC\l1\data\ptr.h(29): may be "void Ptr<Data::CParams>::operator =(T *)"
1> with
1> [
1> T=Data::CParams
1> ]
1> PATH_TO_SRC\l1\data\ptr.h(28): or "void Ptr<Data::CParams>::operator =(const Ptr<Data::CParams> &)"
1> trying to match argument list "(const Ptr<Data::CParams>, const Ptr<Data::CParams>)"
1> PATH_TO_SRC\l1\data\type.h(125): compiling member function "void Data::CTypeImpl<T>::Copy(void **,void *const *) const" template class
1> with
1> [
1> T=const Ptr<Data::CParams>
1> ]
1> PATH_TO_SRC\l1\data\data.h(167): "Data::CTypeImpl<T>"
1> with
1> [
1> T=const Ptr<Data::CParams>
1> ]
1> PATH_TO_SRC\l1\data\data.h(97): "T &Data::CData::GetValue<T>(void)"
1> with
1> [
1> T=const Ptr<Data::CParams>
1> ]
1> PATH_TO_SRC\l3\quests\questmanager.cpp(307): "Data::CData::operator const T(void)<const Ptr<Data::CParams>>"
1> with
1> [
1> T=const Ptr<Data::CParams>
1> ]
I translated messages to English manually, so they can slightly differ from original EN compiler messages.
And finally, if I write explicit template argument:
Data::PParams SGQuest = SGQuests->Get<Data::PParams>(i);
It compiles OK.
The questions are:
How VS2013 template argument guessing differs from VS2008 one? Is there any place (standard, article or smth) that clearly explains, why my old code shouldn't compile? Any reference would be appreciated.
How should I write code in that situations? Do I have to write template arguments explicitly now, or just modify member overloads, or install some update?
P.S. Minimal code is available at https://www.dropbox.com/s/zjohnu5v87tyr2c/ConstOverload.zip?dl=0 . Full source code is at
https://code.google.com/p/deusexmachina/source/browse/branches/Dev/DEM/Src
This problem was solved by adding third overload to CData
template<class T> operator const T&() { return GetValue<T>(); }
Original solution is posted in:
Conversion operator error C2678 in VS2013, works in VS2008
Look there for possible further details.
This compiles:
std::vector<int> value = boost::assign::list_of(1)(2);
But not this:
Constructor(std::vector<int> value)
{
}
Constructor (boost::assign::list_of(1)(2));
Is there a one-liner solution for initializing the vector passed to the constructor?
Better still, if the constructor copies to a class variable by taking a reference instead:
Constructor(std::vector<int>& value)
{
_value = value;
}
UPDATE
If I try the following:
enum Foo
{
FOO_ONE, FOO_TWO
};
class Constructor
{
public:
Constructor(const std::vector<Foo>& value){}
};
Constructor c(std::vector<Foo>(boost::assign::list_of(FOO_ONE)));
I get the compiler error:
error C2440: '<function-style-cast>' : cannot convert from 'boost::assign_detail::generic_list<T>' to 'std::vector<_Ty>'
1> with
1> [
1> T=Foo
1> ]
1> and
1> [
1> _Ty=Foo
1> ]
1> No constructor could take the source type, or constructor overload resolution was ambiguous
This is a annoying problem, we also had some time before. We fixed it by using the convert_to_container method:
Constructor c(boost::assign::list_of(1)(2).convert_to_container<std::vector<int> >() );
There are more issues with std::list using in constructor too. See Pass std::list to constructor using boost's list_of doesn't compile for the appropriate answer.
I'm using this template to make temporary instance of std::vector in-place:
#include <vector>
namespace Util {
//init vector
template <typename ELEMENT_TYPE > struct vector_of
: public std::vector<ELEMENT_TYPE>
{
vector_of(const ELEMENT_TYPE& t)
{
(*this)(t);
}
vector_of& operator()(const ELEMENT_TYPE& t)
{
this->push_back(t);
return *this;
}
};
}//namespace Util
Usage would look like this:
Constructor (Util::vector_of<int>(1)(2));
Constructor signature would look like this:
Constructor(const std::vector<int>& value)
{
_value = value;
}
Wordy title, yes, but I was unsure how else to say it. Suppose I have a container class which has two template parameters, the first of which is a type, the second of which is the size of the local storage for the container.
Now we have multiple containers with a different container storage size. Essentially, the container functions (all the public ones, anyway) only really care about T; N is only used to allocate local storage (an allocator is used if N is not enough).
I have put together a simple example implementation that showcases the problem I am having.
#include <iostream>
template <typename T, size_t N = 10>
class TestArray
{
public:
T Local[N];
class Iterator
{
public:
T* Array;
int Index;
Iterator() : Array(NULL), Index(-1) { }
Iterator(T* _array, int _index) : Array(_array), Index(_index) { }
bool operator == (const Iterator& _other) const
{
return _other.Index == Index && _other.Array == Array;
}
void Next() { ++Index; }
void Prev() { --Index; }
T& Get() { return Array[Index]; }
};
T& operator [] (const int _index) { return Local[_index]; }
Iterator Begin() { return Iterator(Local, 0); }
Iterator End() { return Iterator(Local, N); }
template <size_t _N>
void Copy(const TestArray<T, _N> &_other, int _index, int _count)
{
int i;
for (i = 0; i < _count; i++)
Local[_index + i] = _other[i];
}
};
This is really a two part question. I will concern this question only with the first part, and ask another regarding the second. I tried using it as follows:
int main() {
TestArray<int> testArray1;
TestArray<int, 25> testArray2;
TestArray<int>::Iterator itr1;
TestArray<int, 25>::Iterator itr2;
itr1 = testArray1.Begin();
for (itr1 = testArray1.Begin(); itr1 != testArray1.End(); itr1.Next())
{
itr1.Get() = itr1.Index;
}
testArray2.Copy(testArray1, 0, 10);
for (itr2 = testArray2.Begin(); itr2 != testArray2.End(); itr2.Next())
{
std::cout << itr2.Get() << std::endl;
}
return 0;
}
Here is an IDEONE link: http://ideone.com/1XKwD
When compiled with gcc-4.3.4, I get the following.
prog.cpp: In member function ‘void TestArray<T, N>::Copy(const TestArray<T, _N>&, int, int) [with unsigned int _N = 10u, T = int, unsigned int N = 25u]’:
prog.cpp:82: instantiated from here
prog.cpp:63: error: passing ‘const TestArray<int, 10u>’ as ‘this’ argument of ‘T& TestArray<T, N>::operator[](int) [with T = int, unsigned int N = 10u]’ discards qualifiers
When compiled with VS2010, I get the following.
1>------ Build started: Project: testunholytemplatemess, Configuration: Debug Win32 ------
1> main.cpp
1>c:\users\james\documents\testproj\testunholytemplatemess\testunholytemplatemess\main.cpp(63): error C2678: binary '[' : no operator found which takes a left-hand operand of type 'const TestArray<T>' (or there is no acceptable conversion)
1> with
1> [
1> T=int
1> ]
1> c:\users\james\documents\testproj\testunholytemplatemess\testunholytemplatemess\main.cpp(44): could be 'int &TestArray<T>::operator [](const int)'
1> with
1> [
1> T=int
1> ]
1> while trying to match the argument list '(const TestArray<T>, int)'
1> with
1> [
1> T=int
1> ]
1> c:\users\james\documents\testproj\testunholytemplatemess\testunholytemplatemess\main.cpp(82) : see reference to function template instantiation 'void TestArray<T,N>::Copy<10>(const TestArray<T> &,int,int)' being compiled
1> with
1> [
1> T=int,
1> N=25
1> ]
Maybe I'm being thick, but I'm failing to interpret what either of these is actually trying to tell me (still somewhat new to templates). I also fail to understand why the operator [] method should really care about N, or the fact that I'm calling operator [] on a container with a different N value. If you change _other[i] to _other.Local[i], it works fine.
Does anyone have any suggestions?
You have to overload two versions for the []-operator, a const one and a non-const one:
T & operator [] (size_t _index) { return Local[_index]; }
const T & operator [] (size_t _index) const { return Local[_index]; }
Your constant Copy function is only allowed to use the second, constant version!