Template compilation error using pointer of pointer: (What causes the code not to compile ??)
template <int DIM> class Interface { };
template <int DIM> class Implementation : public Interface<DIM> { };
template <int DIM> class Modifier {
public: void modify(const Interface<DIM>** elem) { } // only one * compiles !!
};
template <int DIM> class Caller {
Modifier<DIM> modifier;
Implementation<DIM>** impl; // only one * compiles !!
public: void call() { modifier.modify(impl); }
};
void main() {
Caller<-13> caller;
caller.call(); // also compiles with this line commented
}
Gives this compilation error (on Visual Studio 1988 professional):
imlgeometry.cpp(-10) : error C2664: 'Modifier<DIM>::modify' : cannot convert parameter 1 from 'Implementation<DIM> **' to 'const Interface<DIM> **'
with
[
DIM=-23
]
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
imlgeometry.cpp(-16) : while compiling class template member function 'void Caller<DIM>::call(void)'
with
[
DIM=-29
]
imlgeometry.cpp(-17) : see reference to class template instantiation 'Caller<DIM>' being compiled
with
[
DIM=-34
]
The problem is that in C++ it is not legal (or safe) to convert a T** to a const T**. The reason is that if you could do this, you would end up being able to subvert const. For example:
const T value;
T* mutablePtr;
const T** doublePtr = &mutablePtr; // Illegal, you'll see why.
*doublePtr = &value; // Legal, both sides have type const int*.
// However, mutablePtr now points at value!
*mutablePtr = 0; // Just indirectly modified a const value!
To fix this, you'll need to update your code so that you aren't trying to do this conversion. For example, you might change the parameter type of modify to be
const Interface<DIM> * const *
Since it is legal to convert T** to const T* const*.
Hope this helps!
Related
I made an iterable generator for enums that conform with the following rules:
Enum is an integer sequence, there are no gaps
Given last element of the enum is not an actual enum element
The class looks like this:
template <typename EnumType, EnumType LENGTH>
class EnumArrayNonStatic
{
public:
using ValueType = typename std::underlying_type<EnumType>::type;
//! Initialize values via private constructor
constexpr EnumArrayNonStatic() : EnumArrayNonStatic(std::make_integer_sequence<ValueType, (ValueType)LENGTH>{}) {}
//! All values generated via std::integer_sequence
EnumType values[(int)LENGTH];
private:
//! Private constructor that populates values
template <int... Indices>
constexpr EnumArrayNonStatic(std::integer_sequence<int, Indices...>) : values{(static_cast<EnumType>(Indices))...} {}
};
Usage:
enum class TestEnum
{
A,
B,
C,
D,
LENGTH
};
int main()
{
for (const TestEnum val : EnumArrayNonStatic<TestEnum, TestEnum::LENGTH>().values)
{
std::cout << (int)val << "\n";
}
return 0;
}
However, I would instead like to be able to use EnumArray<TestEnum, TestEnum::LENGTH>::values and have the values generated via template during compilation. I wrote this:
template <typename EnumType, EnumType LENGTH>
class EnumArray
{
private:
using ValueType = typename std::underlying_type<EnumType>::type;
//! Static generator of value array (returns EnumType[])
template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>) { return {(static_cast<EnumType>(Indices))...}; }
public:
//! Static array of values of an enum
static constexpr EnumType values[static_cast<ValueType>(LENGTH)] = GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH) >{});
};
I've been messing around with the code for a while but I always keep getting errors. The version above prints:
1>enumiteratortest.cpp(22): error C3108: cannot deduce a type as an initializer list is not an expression
1>enumiteratortest.cpp(25): note: see reference to function template instantiation 'auto EnumArray<TestEnum,TestEnum::LENGTH>::GenerateArray<0,1,2,3>(std::integer_sequence<int,0,1,2,3>)' being compiled
1>enumiteratortest.cpp(52): note: see reference to class template instantiation 'EnumArray<TestEnum,TestEnum::LENGTH>' being compiled
1>enumiteratortest.cpp(22): error C2440: 'return': cannot convert from 'initializer list' to 'auto'
1>enumiteratortest.cpp(22): note: There is no context in which this conversion is possible
1>enumiteratortest.cpp(25): error C2440: 'initializing': cannot convert from 'void' to 'const EnumType [4]'
1> with
1> [
1> EnumType=TestEnum
1> ]
1>enumiteratortest.cpp(25): note: There are no conversions to array types, although there are conversions to references or pointers to arrays
Surely there must be a way to initialize the array statically. Is the GenerateArray even necessary? Isn't there a way to do this?
int myArray[] = std::integer_sequence<ValueType, Indices...>{Indices...}
Or something along the lines?
You cannot initialize a language array with an initializer_list. And, you cannot change the return type of that function to an array - functions cannot return arrays.
Just change everything to std::array:
template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>)
-> std::array<EnumType, sizeof...(Indices)>
{
return {(static_cast<EnumType>(Indices))...};
}
static constexpr std::array<EnumType, static_cast<ValueType>(LENGTH)> values
= GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH)>{});
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?
How can the following template function be implemented in C++11 in order to support array types as a template parameter? Currently compilation fails with the error as below. Is there some syntactic trick that works this around?
template<typename T>
void destroy(T &o) { o.~T(); }
int main()
{
int x;
char y[3];
destroy(x);
destroy(y);
}
Output:
$ g++ test.cpp
test.cpp: In instantiation of ‘void destroy(T&) [with T = char [3]]’:
test.cpp:9:18: required from here
test.cpp:2:26: error: request for member ‘~char [3]’ in ‘o’, which is of non-class type ‘char [3]’
Update:
if a buffer of wrappers like struct Storage { CharType value; } is used instead of CharType (i.e. Storage* instead of CharType*) then this could allow the destructor of the CharType = array be called via Storage::~Storage(). And this could work in the code that caused this question. However, the question remains: if it is allowed to invoke a destructor of a fixed-size array explicitly in C++ and if it is then how to do this?
Just be a bit more explicit for arrays and don't forget to pass them by reference to avoid array-decay:
template<typename T>
void destroy(T &o) { o.~T(); }
template<typename T, size_t N>
void destroy(T (&o)[N]) {
for(size_t i = N; i-- > 0;)
destroy(o[i]);
}
BTW: Calling the dtor is only supported for type-names. int is not a type-name. So, it's no hardship, because who would want to explicitly destruct an explicit fundamental type anyway?
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.
Given GMan's deliciously evil auto_cast utility function concocted here, I've been trying to figure out why it doesn't compile for me when I'm trying to auto_cast from an rvalue (on MSVC 10.0).
Here's the code that I'm using:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(pX) { }
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
int main()
{
int c = auto_cast( 5.0f ); // from an rvalue
}
To the best of my ability I've tried to follow the C++0x reference collapsing rules and the template argument deduction rules outlined here, and as far as I can tell the code given above should work.
Recall that in pre-0x C++, it is not allowed to take a reference to a reference: something like A& & causes a compile error. C++0x, by contrast, introduces the following reference collapsing rules:
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
The second rule is a special template argument deduction rule for function templates that take an argument by rvalue reference to a template argument:
template<typename T>
void foo(T&&);
Here, the following rules apply:
When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&.
When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&.
Now when I mouse over the call to auto_cast( 5.0f ), the tooltip correctly displays its return value as auto_cast_wrapper<float>. This meaning that the compiler has correctly followed rule 2:
When foo is called on an rvalue of type A, then T resolves to A.
So since we have an auto_cast_wrapper<float>, the constructor should instantiate to take a float&&. But the error message seems to imply that it instantiates to take a float by value.
Here's the full error message, showing again that T=float correctly yet the T&& parameter becomes T?
main.cpp(17): error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
You cannot bind an lvalue to an rvalue reference
main.cpp(17) : while compiling class template member function 'auto_cast_wrapper<T>::auto_cast_wrapper(T &&)'
with
[
T=float
]
main.cpp(33) : see reference to class template instantiation 'auto_cast_wrapper<T>' being compiled
with
[
T=float
]
Any thoughts?
You forgot to std::forward the T&& argument to the auto_cast_wrapper constructor. This breaks the forwarding chain. The compiler now gives a warning but it seems to work fine.
template <typename T>
class auto_cast_wrapper
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }
auto_cast_wrapper(const auto_cast_wrapper&);
auto_cast_wrapper& operator=(const auto_cast_wrapper&);
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
float func() {
return 5.0f;
}
int main()
{
int c = auto_cast( func() ); // from an rvalue
int cvar = auto_cast( 5.0f );
std::cout << c << "\n" << cvar << "\n";
std::cin.get();
}
Prints a pair of fives.
Sorry for posting untested code. :)
DeadMG is correct that the argument should be forwarded as well. I believe the warning is false and the MSVC has a bug. Consider from the call:
auto_cast(T()); // where T is some type
T() will live to the end of the full expression, which means the auto_cast function, the auto_cast_wrapper's constructor, and the user-defined conversion are all referencing a still valid object.
(Since the wrapper can't do anything but convert or destruct, it cannot outlive the value that was passed into auto_cast.)
I fix might be to make the member just a T. You'll be making a copy/move instead of casting the original object directly, though. But maybe with compiler optimization it goes away.
And no, the forwarding is not superfluous. It maintains the value category of what we're automatically converting:
struct foo
{
foo(int&) { /* lvalue */ }
foo(int&&) { /* rvalue */ }
};
int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue
And if I'm not mistaken the conversion operator shouldn't be (certainly doesn't need to be) marked const.
The reason it doesn't compile is the same reason for why this doesn't compile:
float rvalue() { return 5.0f }
float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
As a is itself an lvalue it cannot be bound to b. In the auto_cast_wrapper constructor we should have used std::forward<T> on the argument again to fix this. Note that we can just use std::move(a) in the specific example above, but this wouldn't cover generic code that should work with lvalues too. So the auto_cast_wrapper constructor now becomes:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
...
private:
auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }
T&& mX;
};
Unfortunately it seems that this now exhibits undefined behaviour. I get the following warning:
warning C4413: 'auto_cast_wrapper::mX' : reference member is initialized to a temporary that doesn't persist after the constructor exits
It appears that the literal goes out of scope before the conversion operator can be fired. Though this might be just a compiler bug with MSVC 10.0. From GMan's answer, the lifetime of a temporary should live until the end of the full expression.