Ahh, C++ templates...
The code I see,
makes sense to me,
but GCC...
it disagrees.
The following code compiles and runs as expected, but if you uncomment that #define, you get the error, which I don't understand. The symbol iterator still has only one thing it can refer to: the typedef in the superclass. So I guess I have two questions: 1. What do the errors mean? 2. What is the best way to fix them.
#include <map>
#include <string>
#include <cstdio>
using namespace std;
// #define WITH_TEMPLATE 1
#ifdef WITH_TEMPLATE
template <class C>
struct MyClass : public map<string, C>
#else
struct MyClass : public map<string, int>
#endif
{
bool haskey(const string &s)
{
iterator it = find(s);
return (it != end());
}
};
int main()
{
#ifdef WITH_TEMPLATE
MyClass<int> m;
#else
MyClass m;
#endif
m["test"] = 10;
printf("%d %d\n", m.haskey("test"), m.haskey("no"));
}
Errors from GCC:
temp.cc: In member function ‘bool MyClass::haskey(const std::string&)’:
temp.cc:18: error: missing template arguments before ‘it’
temp.cc:18: error: expected `;' before ‘it’
temp.cc:19: error: ‘it’ was not declared in this scope
temp.cc:19: error: there are no arguments to ‘end’ that depend on a template parameter, so a declaration of ‘end’ must be available
temp.cc:19: error: (if you use ‘-fpermissive’, G++ will accept your code, but allowing the use of an undeclared name is deprecated)
You need to change your MyClass::haskey method too.
bool haskey(const string &s)
{
typename MyClass<C>::iterator it = this->find(s);
return (it != this->end());
}
Explanation of such a behavior is in section "Name lookup, templates, and accessing members of base classes" on http://physics.ucsd.edu/students/courses/winter2008/physics141/manuals/rhel-gcc-en-4/c---misunderstandings.html (link from another answer's comment, just in case).
Whole fixed example code: http://ideone.com/G7Rty
iterator it = find(s);
return (it != end());
This line should be as,
#ifdef WITH_TEMPLATE
typename map<string, C>::iterator it = this->find(s);
return (it != this->end());
#else
map<string, int>::iterator it = find(s);
return (it != end());
#endif
Related
#include <map>
#include <memory>
#include <iostream>
using namespace std;
class test
{
public:
test(){}
~test(){}
enum type
{
error = 0
};
private:
shared_ptr<map<int, type>> member_ = make_shared<map<int, type>>();//build error
//shared_ptr<map<type, int>> member_ = make_shared<map<type, int>>();//build ok
};
int main()
{
return 0;
}
when i compile program with "shared_ptr<map<int, type>> member_ = make_shared<map<int, type>>()"
main.cpp:17:63: error: expected ‘;’ at end of member declaration
shared_ptr<map<int, type>> member_ = make_shared<map<int, type>>();
^
main.cpp:17:67: error: expected unqualified-id before ‘>>’ token
shared_ptr<map<int, type>> member_ = make_shared<map<int, type>>();
^
main.cpp:17:58: error: wrong number of template arguments (1, should be at least 2)
shared_ptr<map<int, type>> member_ = make_shared<map<int, type>>();
^
In file included from /usr/include/c++/5/map:61:0,
from main.cpp:1:
/usr/include/c++/5/bits/stl_map.h:96:11: note: provided for ‘template<class _Key, class _Tp, class _Compare, class _Alloc> class std::map’
class map
^
main.cpp:17:42: error: parse error in template argument list
shared_ptr<map<int, type>> member_ = make_shared<map<int, type>>();
^
main.cpp:17:42: error: cannot resolve overloaded function ‘make_shared’ based on conversion to type ‘std::shared_ptr<std::map<int, test::type> >’
Have tried your code with Online C++ Compiler, got the same errors with C++, C++11, and C++14 compatible compilers, but got compiled well with C++17 compatible compiler. This means that your code is not legal in C++ standards prior to C++17. As pointed out by others, the code, however, compiled well on other online compiler sites even with C++11 and C++14 standards. So I would guess the errors are due to supported / unsupported features of compilers.
Well, you are not supposed to initialize class members like this anyway. The initialization of class members should happen in the class constructor, like so:
#include <map>
#include <memory>
#include <iostream>
using namespace std;
class test
{
public:
test() { member_ = make_shared<map<int, type>>(); }
~test() { }
enum type
{
error = 0
};
private:
shared_ptr<map<int, type>> member_;
};
int main()
{
return 0;
}
This compiles fine even in Online C++ Compiler which is the only place we are managing to reproduce the problem.
I have a code in VC2010 which I've reduced to a small example.
Test.h:
#pragma once
template <typename TPixel>
struct Image
{
typedef TPixel PixelType;
};
template <typename TImageType>
struct Operation
{
void DoOperation()
{
ImageType::PixelType value = 0;
// I've done a misprint here. It should be TImageType::PixelType
}
};
Test.cpp:
void Test()
{
typedef Image<char> ImageType;
Operation<ImageType> op;
op.DoOperation();
}
As I expected, that produces an error.
test.h(14): error C2653: 'ImageType' : is not a class or namespace name
Now, let's change test.cpp just a little bit.
typedef Image<char> ImageType;
void Test()
{
Operation<ImageType> op;
op.DoOperation();
}
Now it compiles! Surprisingly enough, ImageType in DoOperation() now matches with the global typedef in test.cpp.
My question: why does it compile? Is that a Visual C++ bug or a standard behavior?
I take it that test.cpp includes test.h before the typedef, so it's actually
#include "test.h"
typedef Image<char> ImageType;
void Test()
{
Operation<ImageType> op;
op.DoOperation();
}
Completed like this, it is indeed a bug, or standard nonconforming behavior with regard to two phase lookup. Names that do not depend on a template parameter should be resolved relative to the point of declaration of the template.
I guess this is known behavior.
I want to iterate over a QMultiMap using
QMultiMap<double, TSortable>::const_iterator it;`
but the compiler complains
error: expected ‘;’ before ‘it’
resulting in a
error: ‘it’ was not declared in this scope
for every usage. I tried ConstIterator, const_iterator and even the slower Iterator without any success. Is it even possible to use Q(Multi)Map with a template class? Why can't I declare an Iterator when definition (as void*) is ok?
I use the following code (include guard omitted):
#include <QtCore/QDebug>
#include <QtCore/QMap>
#include <QtCore/QMultiMap>
#include <limits>
/** TSortable has to implement minDistance() and maxDistance() */
template<class TSortable>
class PriorityQueue {
public:
PriorityQueue(int limitTopCount)
: limitTopCount_(limitTopCount), actMaxLimit_(std::numeric_limits<double>::max())
{
}
virtual ~PriorityQueue(){}
private:
void updateActMaxLimit(){
if(maxMap_.count() < limitTopCount_){
// if there are not enogh members, there is no upper limit for insert
actMaxLimit_ = std::numeric_limits<double>::max();
return;
}
// determine new max limit
QMultiMap<double, TSortable>::const_iterator it;
it = maxMap_.constBegin();
int act = 0;
while(act!=limitTopCount_){
++it;// forward to kMax
}
actMaxLimit_ = it.key();
}
const int limitTopCount_;
double actMaxLimit_;
QMultiMap<double, TSortable> maxMap_;// key=maxDistance
};
GCC gives this error before the one you quoted:
error: need ‘typename’ before ‘QMultiMap<double, TSortable>::const_iterator’ because ‘QMultiMap<double, TSortable>’ is a dependent scope
which explains the problem. Add the typename keyword:
typename QMultiMap<double, TSortable>::const_iterator it;
and it will build.
I want to use a boost::ptr_map inside a specific class which stores instances of itself. However, please consider the following example:
#include <boost/checked_delete.hpp>
#include <boost/ptr_container/ptr_map.hpp>
class foo
{
friend void boost::checked_delete<>(foo*);
~foo() {}
};
int main()
{
boost::checked_delete(new foo); // OK
boost::ptr_map<int, foo> foo_map; // error C2248: 'foo::~foo' : cannot access private member declared in class 'foo'
return 0;
}
The error happens at the following line
// verify that types are complete for increased safety
template<class T> inline void checked_delete(T * x)
{
// intentionally complex - simplification causes regressions
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete x; // error C2248
}
What exactly is going on here? Shouldn't it work? I assume that the problem is that templates are defined in the compilation unit they are included in and boost::checked_delete is called from another compilation unit in the implementation source of bosst::ptr_map. So, it's not the same function I declared as a friend.
However, is there a workaround for this problem?
Try this syntax when declaring the friend:
template <class T>
friend void boost::checked_delete(T*);
Here is the start of the huge error message* from GCC, which is the start of the chain of instantiations (usually, and in this case):
In file included from main.cpp:1:0:
main.cpp: In function 'void boost::checked_delete(T*) [with T = const foo]':
Adding
friend void boost::checked_delete<>(foo const*);
makes the code compile.
(*): 13 lines and 3510 characters for 270 chars/line
Here’s an example setup… a macro or a template CHECKEXPR_RETURNVAL(EXPR,VAL) that checks that EXPR is TRUE while returning VAL.
This is useful in a variety of places -- like in this highly simplified example:
#define ISPOW2(VAL) ((0!=VAL)&&(0==(VAL&(VAL-1))))
#define _ALIGNPOW2(VAL,ALIGN) ((VAL+(ALIGN-1))&(~(ALIGN-1)))
#define ALIGNPOW2(VAL,ALIGN) CHECKEXPR_RETURNVAL( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
So, the difficulty is this: I want to do compile time checks if possible, and if the value is not a constant that is determinate at compile time, then do a runtime check.
Basically, the idea is to catch bad parameters as soon as possible; if you can catch a bad parameter at compile time that's better than finding out at run time. Also, the compile time version is required for constant initializers.
Here are my two (failed) attempts to make single version work in multiple places (as a constant array size, as an enum initializer, and in a function with variables). Unfortunately, they either work for the compile time only (constant initializer) or the runtime only -- I would like to figure out a version that will work for both.
// CHECKEXPR_RETURNVAL - version "A"
#define CTCHECK_EXPR(EXP)(CTCheckBool<EXP>::ExistsZeroIfTrue)
template <bool bExpression> struct CTCheckBool {};
template <> struct CTCheckBool<true> {enum{ExistsZeroIfTrue=0};};
// Note: Plus ("+") is used rather than comma operator because
// the comma operator can not be used for constant initializers
#define CHECKEXPR_RETURNVAL_A(EXP,VAL) (CTCHECK_EXPR(EXP) + (VAL))
// Test Out version "A" -- works only for Compile Time Constants
#define ALIGNPOW2_A(VAL,ALIGN) CHECKEXPR_RETURNVAL_A( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
char AlignedVar_A[ALIGNPOW2_A(2,8)];
enum { AlignedVal_A = ALIGNPOW2_A(57,16) };
int TestAlignPow2_A(int val, int align)
{return(ALIGNPOW2_A(val,align));} // Compile Error
// CHECKEXPR_RETURNVAL - version "B"
template<typename T> T CHECKEXPR_RETURNVAL_B(bool bExpr,T val)
{ ASSERT(bExpr); return(val); }
// Test Out version "B" -- works only for Runtime Computed Values
#define ALIGNPOW2_B(VAL,ALIGN) CHECKEXPR_RETURNVAL_B( \
ISPOW2(ALIGN) , _ALIGNPOW2(VAL,ALIGN) )
char AlignedVar_B[ALIGNPOW2_B(2,8)]; // Compile Error
enum { AlignedVal_B = ALIGNPOW2_B(57,16) }; // Compile Error
int TestAlignPow2_B(int val, int align)
{return(ALIGNPOW2_B(val,align));}
Unfortunately, neither version works for all three cases. Is there a code structure that will work for all the cases ?
It seems like you would really make use of c++0x constexpr functions...
Well... Not a complete answer, but I think you can derive what you want from this:
#include <stdio.h>
template <int I> struct S{
static void doIt(){
fprintf(stderr, "wow\n");
}
};
template<> void S<0>::doIt(){
fprintf(stderr, "oops\n");
}
#define EXPR(p) S<(int)((bool)(p))>::doIt()
int main(int argc, char** argv){
EXPR((5+2)==7);
EXPR((5+2)==8);
const int a = 58;
EXPR(a==58);
EXPR(58);
return 0;
}
You can get a compiler error based on expression:
#include <stdio.h>
template <int I> struct S{
static void doIt(){
ssdfkjehiu //deliberately invalid code
fprintf(stderr, "oops\n");
}
};
template<> void S<1>::doIt(){
fprintf(stderr, "wow\n");
}
#define EXPR(p) S<(int)((bool)(p))>::doIt()
int main(int argc, char** argv){
EXPR((5+2)==7);
EXPR((5+2)==8);//uncomment it to make code compile
const int a = 58;
EXPR(a==58);
EXPR(58);
return 0;
}
But the line that causes error will be in a middle of a lengthy template error message. Example:
1.cpp(6) : error C2065: 'asdlfkjhasd' : undeclared identifier
1.cpp(4) : while compiling class template member function 'void S<I>::doIt(void)'
with
[
I=0
]
1.cpp(19) : see reference to class template instantiation 'S<I>' being compiled
with
[
I=0
]
1.cpp(6) : error C2146: syntax error : missing ';' before identifier 'fprintf'
As you see, error is caused by line 19, which is mentioned in the middle of the message. This is a bit inconvenient.
I cannot guarantee that both examples doesn't rely on some undefined C++ behavior.
Also, I think that the next code maintainer may not be happy about this...
P.S. I think you should also take a look at boost. If I remember correctly, it had quite a lot of "magic preprocessor macro" (loops, for example), so it is possible that it implemented something similar.
--edit--
Okay, what about this one?:
#include <stdio.h>
#include <string>
template <typename T> void a(T &i){
fprintf(stderr, "variable\n");
}
void a(const char* const i){
fprintf(stderr, "constant\n");
}
void a(bool i){
fprintf(stderr, "constant\n");
}
int main(int argc, char** argv){
int i;
float f;
std::string s;
const char* s1 = "b";
a(3);
a(3+2);
a(1.0f);
a('a');
a("b");
a(i);
a(f);
a(s);
a(s1);
return 0;
}