I have the following code (stripped down version from actual project to reproduce
the issue) that results in a compiler error on RHEL5 (g++ version 4.1.2):
----------- driver (test.cpp)--------------
#include <iostream>
#include <classa.hpp>
#include <func.hpp>
namespace globals {
static int kth(const A& a) {
return kth(a.ival());
}
}
using namespace globals;
int main() {
A a;
std::cout << func(a) << std::endl;
return 0;
}
----------class A (classa.hpp)------------
class A {
public:
A():val(0){}
const int ival() const {return val;}
private:
int val;
};
------- namespace globals (func.hpp) ------
namespace globals {
int kth(const int& c) {
return c;
}
template <class T>
int func(const T& key) {
return kth(key);
}
}
--------------------------------------------
Compiling it using g++ 4.1.2 gives me the following error:
func.hpp: In function ‘int globals::func(const T&) [with T = A]’:
test.cpp:15: instantiated from here
func.hpp:8: error: invalid initialization of reference of type ‘const int&’ from
expression of type ‘const A’
func.hpp:2: error: in passing argument 1 of ‘int globals::kth(const int&)’
Same code compiles and runs perfectly fine on RHEL4 (g++ version 3.4.6)! Any explanations/ideas/suggestions on how to resolve this error(?) on RHEL5 will
be much appreciated!
Edit:
Thanks Sergey. That is the obvious solution that I am aware of already. But I forgot to add that the restriction is that func.hpp cannot be edited (for e.g., its 3rd party write-protected). Any workarounds?
Here's what happens. When the function func() is defined, the compiler doesn't know about the function kth(const A&) yet because it is defined later in the code. So when it encounters a reference to kth() inside func(), it assumes that it is a reference to kth(const int&). Now when func() is actually instantiated, it fails to compile it because T is A, not int. I am not sure why it works in another version of the compiler, but I think it is because it actually starts resolving references when a template function is instantiated, not when it is declared. But this looks like a bug in the older version because with such behavior a function definition changes depending on where it is instantiated from, which is very confusing.
The only way to fix your code that it works with any compiler would be to put the definition of kth(const A&) between kth(const int&) and func() or a forward declaration of kth(const A&) somewhere above func().
Update
With the restriction of not editing func.hpp the best workaround I can think of is to create a custom header file with something like this:
#include <classa.hpp>
namespace globals {
static int kth(const A& a); // defined later, but used by func.hpp
}
#include <func.hpp>
I also don't see why kth(const A&) is defined as static, but used by a global header. I'd rather put it into the classa.cpp and its declaration into the classa.hpp. But this may be some design feature or artifact I am not aware of.
Related
Please take a look to this code snippet. I know it does not make much sense, it is just intended to illustrate the problem I am encountering:
#include <iostream>
using namespace std;
struct tBar
{
template <typename T>
void PrintDataAndAddress(const T& thing)
{
cout << thing.mData;
PrintAddress<T>(thing);
}
private:
// friend struct tFoo; // fixes the compilation error
template <typename T>
void PrintAddress(const T& thing)
{
cout << " - " << &thing << endl;
}
};
struct tFoo
{
friend void tBar::PrintDataAndAddress<tFoo>(const tFoo&);
private:
int mData = 42;
};
struct tWidget
{
int mData = 666;
};
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
bar.PrintDataAndAddress(tFoo()); // Compilation error
return 0;
}
The code above triggers the following error:
source_file.cpp:10:3: error: 'PrintAddress' is a private member of 'tBar'
PrintAddress(thing);
source_file.cpp:42:6: note: in instantiation of function template >specialization 'tBar::PrintDataAndAddress' requested here
bar.PrintDataAndAddress(tFoo()); // Compilation error
source_file.cpp:17:7: note: declared private here
void PrintAddress(const T& thing)
but only in Clang++. GCC and MSVC are fine with it (you can quickly test that by pasting that code in http://rextester.com/l/cpp_online_compiler_clang)
It seems as if tBar::PrintDataAndAddress<tFoo>(const tFoo&) is using the same access as tFoo, where it is befriended. I know this because befriending tFoo in tBar fixes this issue. The problem also goes away if tBar::PrintDataAndAddress is a non-template function.
I have not been able to find anything in the Standard that explains this behavior. I believe it could be a bad interpretation of 14.6.5 - temp.inject, but I can't claim I have read all of it.
Does anyone know if Clang is right failing to compile the above code? Can you please quote the relevant C++ standard text if that is the case?
It seems that for this problem to happen, the private member being accessed needs to be a template function. e.g., in the example above, if
we make PrintAddress a non-template function, the code will compile without errors.
Forcing the compiler to instantiate tBar::PrintDataAndAddress<tFoo> before using it solves the problem.
int main()
{
tBar bar;
bar.PrintDataAndAddress(tWidget()); // Fine
auto x = &tBar::PrintDataAndAddress<tFoo>; // <= make it work....
bar.PrintDataAndAddress(tFoo()); // Now fine
return 0;
}
It seems to be a compiler promlem as it looks quite similar to this:
In C++, why isn't it possible to friend a template class member function using the template type of another class?
To be a little bit more precise... In the line bar.PrintDataAndAddress(tFoo()); the compiler has to instanciate the memberfunction tBar::PrintDataAndAddress<tFoo> and at the same time it has to resolve the friend declaration. That are internaly two seperate steps. Apparent the compiler doesn't do it in the rigth order when written in one expression. To force the compiler to instantiate bar.PrintDataAndAddress(tFoo()) first by access to the function pointer these two steps are in the right order.
Try adding this before the friend function
template <typename tFoo>
Code:
#include <tr1/functional>
class Test
{
public:
Test() { ; }
virtual void foo() = 0;
};
void someFunc(Test& j)
{
j.foo();
}
void func(Test& j)
{
std::tr1::bind(someFunc, std::tr1::ref(j));
}
Using g++ 4.8.1 on Linux, compiling with --std=c++11 I get:
In file included from foo.cpp:1:0:
/usr/include/c++/4.8.1/tr1/functional: In instantiation of ‘class std::tr1::reference_wrapper<Test>’:
foo.cpp:17:44: required from here
/usr/include/c++/4.8.1/tr1/functional:495:9: error: cannot allocate an object of abstract type ‘Test’
operator()(_Args&... __args) const
^
foo.cpp:3:7: note: because the following virtual functions are pure within ‘Test’:
class Test
^
foo.cpp:7:18: note: virtual void Test::foo()
virtual void foo() = 0;
^
This doesn't seem to make any sense. Using the corresponding boost classes works fine. Can someone confirm this is a TR1 bug in G++ 4.8.1?
The libstdc++ tr1::reference_wrapper implementation has this:
template<typename... _Args>
typename result_of<_M_func_type(_Args...)>::type
operator()(_Args&... __args) const
{
return __invoke(get(), __args...);
}
The result_of expression uses a by-value _M_func_type parameter (which is the template parameter of the reference_wrapper i.e. Test), so it tries to form the function type Test(), which uses a by-value Test return type, which is invalid for an incomplete or abstract type. I think I fixed this for std::reference_wrapper ages ago, it needs to use result_of<_M_func_type&(Args...)>.
The TR1 implementation in libstdc++ is not really maintained any longer - TR1 served its purpose but its time has passed.
I'm wondering if the following code should be considered valid c++, gcc and clang both choke on it while Microsoft and the embedded compiler (green hills) our project uses seem to parse this without problems. This line of code gives gcc and clang trouble:
foo().Bar<int>();
It appears gcc thinks the < is a less than operator instead of specifying a template argument. If Bar is changed to not be a template function gcc accepts it.
class Test1
{
public:
template<typename U>
static void Bar() {}
};
template<typename T>
class Test2
{
public:
Test2()
{
foo().Bar<int>();
}
Test1 foo() { return t; }
Test1 t;
};
int main()
{
Test2<int> t;
}
From my research calling a static member function on an object instance is valid c++. So what are you thoughts? Here is gcc's error:
Error.cpp: In constructor ‘Test2<T>::Test2()’:
Error.cpp:14:17: error: expected primary-expression before ‘int’
foo().Bar<int>();
^
Error.cpp:14:17: error: expected ‘;’ before ‘int’
The correct syntax is:
foo().template Bar<int>();
(Adding to #Dieter answer)
Otherwise you have to use a temporary:
Test1 tt = foo();
tt.Bar<int>();
(in this case you could have simply used t instead of tt, but that was not the point, of course).
This small test program:
#include <functional>
//template<class T> // <-- with this, gcc compiles ok
template<class T=void>
struct c{
std::function<int(int)> f = [](int i){return i+i;};
};
int main() {};
Clang-3.2 compiles it ok, but from GCC 4.7.1 and 4.8 I am getting strange error:
t.cc:6:31: error: default argument for template parameter for class enclosing ‘struct __lambda0’
function<int(int)> f = [](int i){return i+i;};
^
Is this one of those obscure C++ rules exceptions that nobody knows about or is it a GCC bug?
EDIT
Looks like a bug. I've filed bug report
I think this is a g++ bug with default member initialization. I'm not positive about this, and so have the following supporting evidence:
template<class T=void>
struct c {
std::function<int(int)> f;
c() : f([](int i){return i+i;}) {
}
};
int main() {}
If that works, what you're doing should work too. And it does, even if you construct a c.
Personally, I think default member initialization should be used sparingly and with care. I think it's really easy to create a lot of confusion with it because most people expect all the initialization to be done in the constructor, and member initializers are not necessarily anywhere near any constructor. So they can leave someone scratching their head wondering how some member gets a particular value.
I can see cases, especially with simple, mostly-data classes for which it would work pretty well. But mostly, I think if you have a constructor body of any kind, you should probably not be using default member initialization.
This code will get error anyway on gcc. Yes, without default argument it can be compiled. It can be compiled because struct c isn't used anywhere. But if you try to create instance of this struct, you will get error.
#include <functional>
template<class T>
struct c {
std::function<int(int)> f = [](int i){return i+i;};
};
int main() {
c<int> _c; // error
}
It looks like a bug of gcc. This way can help to avoid problem.
#include <functional>
#include <iostream>
template<class T=void>
struct c {
c() : f([](int i){return i+i;}) {
}
std::function<int(int)> f;
};
int main() {
c<> _c;
std::cout << _c.f(10) << std::endl;
}
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